@webex/plugin-meetings 3.0.0-beta.39 → 3.0.0-beta.391

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 (393) hide show
  1. package/README.md +58 -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/events.js +45 -0
  11. package/dist/breakouts/events.js.map +1 -0
  12. package/dist/breakouts/index.js +671 -81
  13. package/dist/breakouts/index.js.map +1 -1
  14. package/dist/breakouts/utils.js +45 -1
  15. package/dist/breakouts/utils.js.map +1 -1
  16. package/dist/common/errors/no-meeting-info.js +51 -0
  17. package/dist/common/errors/no-meeting-info.js.map +1 -0
  18. package/dist/common/errors/reclaim-host-role-errors.js +158 -0
  19. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -0
  20. package/dist/common/errors/webex-errors.js +48 -7
  21. package/dist/common/errors/webex-errors.js.map +1 -1
  22. package/dist/common/logs/logger-proxy.js +1 -1
  23. package/dist/common/logs/logger-proxy.js.map +1 -1
  24. package/dist/common/logs/request.js +5 -1
  25. package/dist/common/logs/request.js.map +1 -1
  26. package/dist/common/queue.js +24 -9
  27. package/dist/common/queue.js.map +1 -1
  28. package/dist/config.js +5 -10
  29. package/dist/config.js.map +1 -1
  30. package/dist/constants.js +242 -33
  31. package/dist/constants.js.map +1 -1
  32. package/dist/controls-options-manager/enums.js +14 -2
  33. package/dist/controls-options-manager/enums.js.map +1 -1
  34. package/dist/controls-options-manager/index.js +109 -15
  35. package/dist/controls-options-manager/index.js.map +1 -1
  36. package/dist/controls-options-manager/types.js +7 -0
  37. package/dist/controls-options-manager/types.js.map +1 -0
  38. package/dist/controls-options-manager/util.js +309 -18
  39. package/dist/controls-options-manager/util.js.map +1 -1
  40. package/dist/index.js +110 -2
  41. package/dist/index.js.map +1 -1
  42. package/dist/interceptors/index.js +15 -0
  43. package/dist/interceptors/index.js.map +1 -0
  44. package/dist/interceptors/locusRetry.js +93 -0
  45. package/dist/interceptors/locusRetry.js.map +1 -0
  46. package/dist/interpretation/collection.js +23 -0
  47. package/dist/interpretation/collection.js.map +1 -0
  48. package/dist/interpretation/index.js +380 -0
  49. package/dist/interpretation/index.js.map +1 -0
  50. package/dist/interpretation/siLanguage.js +25 -0
  51. package/dist/interpretation/siLanguage.js.map +1 -0
  52. package/dist/locus-info/controlsUtils.js +91 -2
  53. package/dist/locus-info/controlsUtils.js.map +1 -1
  54. package/dist/locus-info/index.js +386 -62
  55. package/dist/locus-info/index.js.map +1 -1
  56. package/dist/locus-info/infoUtils.js +7 -1
  57. package/dist/locus-info/infoUtils.js.map +1 -1
  58. package/dist/locus-info/mediaSharesUtils.js +71 -1
  59. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  60. package/dist/locus-info/parser.js +249 -72
  61. package/dist/locus-info/parser.js.map +1 -1
  62. package/dist/locus-info/selfUtils.js +89 -14
  63. package/dist/locus-info/selfUtils.js.map +1 -1
  64. package/dist/media/index.js +65 -102
  65. package/dist/media/index.js.map +1 -1
  66. package/dist/media/properties.js +73 -124
  67. package/dist/media/properties.js.map +1 -1
  68. package/dist/mediaQualityMetrics/config.js +135 -330
  69. package/dist/mediaQualityMetrics/config.js.map +1 -1
  70. package/dist/meeting/in-meeting-actions.js +86 -2
  71. package/dist/meeting/in-meeting-actions.js.map +1 -1
  72. package/dist/meeting/index.js +4075 -2827
  73. package/dist/meeting/index.js.map +1 -1
  74. package/dist/meeting/locusMediaRequest.js +292 -0
  75. package/dist/meeting/locusMediaRequest.js.map +1 -0
  76. package/dist/meeting/muteState.js +224 -136
  77. package/dist/meeting/muteState.js.map +1 -1
  78. package/dist/meeting/request.js +177 -152
  79. package/dist/meeting/request.js.map +1 -1
  80. package/dist/meeting/util.js +672 -417
  81. package/dist/meeting/util.js.map +1 -1
  82. package/dist/meeting-info/index.js +73 -7
  83. package/dist/meeting-info/index.js.map +1 -1
  84. package/dist/meeting-info/meeting-info-v2.js +192 -51
  85. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  86. package/dist/meeting-info/util.js +1 -1
  87. package/dist/meeting-info/util.js.map +1 -1
  88. package/dist/meeting-info/utilv2.js +36 -36
  89. package/dist/meeting-info/utilv2.js.map +1 -1
  90. package/dist/meetings/collection.js +39 -0
  91. package/dist/meetings/collection.js.map +1 -1
  92. package/dist/meetings/index.js +484 -119
  93. package/dist/meetings/index.js.map +1 -1
  94. package/dist/meetings/meetings.types.js +7 -0
  95. package/dist/meetings/meetings.types.js.map +1 -0
  96. package/dist/meetings/request.js +2 -0
  97. package/dist/meetings/request.js.map +1 -1
  98. package/dist/meetings/util.js +73 -7
  99. package/dist/meetings/util.js.map +1 -1
  100. package/dist/member/index.js +58 -0
  101. package/dist/member/index.js.map +1 -1
  102. package/dist/member/types.js +25 -0
  103. package/dist/member/types.js.map +1 -0
  104. package/dist/member/util.js +132 -25
  105. package/dist/member/util.js.map +1 -1
  106. package/dist/members/collection.js +10 -0
  107. package/dist/members/collection.js.map +1 -1
  108. package/dist/members/index.js +102 -6
  109. package/dist/members/index.js.map +1 -1
  110. package/dist/members/request.js +106 -38
  111. package/dist/members/request.js.map +1 -1
  112. package/dist/members/types.js +15 -0
  113. package/dist/members/types.js.map +1 -0
  114. package/dist/members/util.js +326 -232
  115. package/dist/members/util.js.map +1 -1
  116. package/dist/metrics/constants.js +18 -1
  117. package/dist/metrics/constants.js.map +1 -1
  118. package/dist/metrics/index.js +1 -446
  119. package/dist/metrics/index.js.map +1 -1
  120. package/dist/multistream/mediaRequestManager.js +223 -32
  121. package/dist/multistream/mediaRequestManager.js.map +1 -1
  122. package/dist/multistream/receiveSlot.js +10 -0
  123. package/dist/multistream/receiveSlot.js.map +1 -1
  124. package/dist/multistream/receiveSlotManager.js +39 -36
  125. package/dist/multistream/receiveSlotManager.js.map +1 -1
  126. package/dist/multistream/remoteMedia.js +3 -1
  127. package/dist/multistream/remoteMedia.js.map +1 -1
  128. package/dist/multistream/remoteMediaGroup.js +76 -5
  129. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  130. package/dist/multistream/remoteMediaManager.js +366 -104
  131. package/dist/multistream/remoteMediaManager.js.map +1 -1
  132. package/dist/multistream/sendSlotManager.js +255 -0
  133. package/dist/multistream/sendSlotManager.js.map +1 -0
  134. package/dist/reachability/clusterReachability.js +356 -0
  135. package/dist/reachability/clusterReachability.js.map +1 -0
  136. package/dist/reachability/index.js +263 -390
  137. package/dist/reachability/index.js.map +1 -1
  138. package/dist/reachability/request.js +6 -4
  139. package/dist/reachability/request.js.map +1 -1
  140. package/dist/reachability/util.js +29 -0
  141. package/dist/reachability/util.js.map +1 -0
  142. package/dist/reconnection-manager/index.js +266 -202
  143. package/dist/reconnection-manager/index.js.map +1 -1
  144. package/dist/recording-controller/index.js +21 -2
  145. package/dist/recording-controller/index.js.map +1 -1
  146. package/dist/recording-controller/util.js +9 -8
  147. package/dist/recording-controller/util.js.map +1 -1
  148. package/dist/roap/index.js +51 -28
  149. package/dist/roap/index.js.map +1 -1
  150. package/dist/roap/request.js +48 -64
  151. package/dist/roap/request.js.map +1 -1
  152. package/dist/roap/turnDiscovery.js +220 -70
  153. package/dist/roap/turnDiscovery.js.map +1 -1
  154. package/dist/rtcMetrics/constants.js +12 -0
  155. package/dist/rtcMetrics/constants.js.map +1 -0
  156. package/dist/rtcMetrics/index.js +179 -0
  157. package/dist/rtcMetrics/index.js.map +1 -0
  158. package/dist/statsAnalyzer/index.js +357 -295
  159. package/dist/statsAnalyzer/index.js.map +1 -1
  160. package/dist/statsAnalyzer/mqaUtil.js +296 -156
  161. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  162. package/dist/types/annotation/annotation.types.d.ts +42 -0
  163. package/dist/types/annotation/constants.d.ts +31 -0
  164. package/dist/types/annotation/index.d.ts +117 -0
  165. package/dist/types/breakouts/events.d.ts +8 -0
  166. package/dist/types/breakouts/utils.d.ts +14 -0
  167. package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
  168. package/dist/types/common/errors/reclaim-host-role-errors.d.ts +60 -0
  169. package/dist/types/common/errors/webex-errors.d.ts +25 -1
  170. package/dist/types/common/logs/request.d.ts +2 -0
  171. package/dist/types/common/queue.d.ts +9 -7
  172. package/dist/types/config.d.ts +2 -7
  173. package/dist/types/constants.d.ts +203 -31
  174. package/dist/types/controls-options-manager/enums.d.ts +11 -1
  175. package/dist/types/controls-options-manager/index.d.ts +17 -1
  176. package/dist/types/controls-options-manager/types.d.ts +43 -0
  177. package/dist/types/controls-options-manager/util.d.ts +1 -7
  178. package/dist/types/index.d.ts +6 -5
  179. package/dist/types/interceptors/index.d.ts +2 -0
  180. package/dist/types/interceptors/locusRetry.d.ts +27 -0
  181. package/dist/types/interpretation/collection.d.ts +5 -0
  182. package/dist/types/interpretation/index.d.ts +5 -0
  183. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  184. package/dist/types/locus-info/index.d.ts +57 -4
  185. package/dist/types/locus-info/parser.d.ts +66 -6
  186. package/dist/types/media/index.d.ts +2 -0
  187. package/dist/types/media/properties.d.ts +34 -49
  188. package/dist/types/mediaQualityMetrics/config.d.ts +99 -223
  189. package/dist/types/meeting/in-meeting-actions.d.ts +86 -2
  190. package/dist/types/meeting/index.d.ts +567 -496
  191. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  192. package/dist/types/meeting/muteState.d.ts +93 -25
  193. package/dist/types/meeting/request.d.ts +64 -43
  194. package/dist/types/meeting/util.d.ts +117 -1
  195. package/dist/types/meeting-info/index.d.ts +13 -1
  196. package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
  197. package/dist/types/meetings/collection.d.ts +17 -0
  198. package/dist/types/meetings/index.d.ts +113 -21
  199. package/dist/types/meetings/meetings.types.d.ts +4 -0
  200. package/dist/types/member/index.d.ts +14 -0
  201. package/dist/types/member/types.d.ts +32 -0
  202. package/dist/types/members/collection.d.ts +5 -0
  203. package/dist/types/members/index.d.ts +35 -2
  204. package/dist/types/members/request.d.ts +73 -9
  205. package/dist/types/members/types.d.ts +25 -0
  206. package/dist/types/members/util.d.ts +214 -1
  207. package/dist/types/metrics/constants.d.ts +17 -0
  208. package/dist/types/metrics/index.d.ts +4 -111
  209. package/dist/types/multistream/mediaRequestManager.d.ts +72 -3
  210. package/dist/types/multistream/receiveSlot.d.ts +7 -3
  211. package/dist/types/multistream/receiveSlotManager.d.ts +14 -4
  212. package/dist/types/multistream/remoteMedia.d.ts +3 -31
  213. package/dist/types/multistream/remoteMediaGroup.d.ts +2 -9
  214. package/dist/types/multistream/remoteMediaManager.d.ts +62 -2
  215. package/dist/types/multistream/sendSlotManager.d.ts +70 -0
  216. package/dist/types/reachability/clusterReachability.d.ts +109 -0
  217. package/dist/types/reachability/index.d.ts +60 -95
  218. package/dist/types/reachability/request.d.ts +3 -1
  219. package/dist/types/reachability/util.d.ts +8 -0
  220. package/dist/types/reconnection-manager/index.d.ts +19 -0
  221. package/dist/types/recording-controller/index.d.ts +15 -1
  222. package/dist/types/recording-controller/util.d.ts +5 -4
  223. package/dist/types/roap/index.d.ts +2 -1
  224. package/dist/types/roap/request.d.ts +9 -8
  225. package/dist/types/roap/turnDiscovery.d.ts +39 -5
  226. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  227. package/dist/types/rtcMetrics/index.d.ts +61 -0
  228. package/dist/types/statsAnalyzer/index.d.ts +34 -12
  229. package/dist/types/statsAnalyzer/mqaUtil.d.ts +28 -4
  230. package/dist/types/webinar/collection.d.ts +16 -0
  231. package/dist/types/webinar/index.d.ts +5 -0
  232. package/dist/webinar/collection.js +44 -0
  233. package/dist/webinar/collection.js.map +1 -0
  234. package/dist/webinar/index.js +69 -0
  235. package/dist/webinar/index.js.map +1 -0
  236. package/package.json +22 -19
  237. package/src/annotation/annotation.types.ts +50 -0
  238. package/src/annotation/constants.ts +36 -0
  239. package/src/annotation/index.ts +328 -0
  240. package/src/breakouts/README.md +35 -11
  241. package/src/breakouts/breakout.ts +67 -9
  242. package/src/breakouts/events.ts +56 -0
  243. package/src/breakouts/index.ts +558 -59
  244. package/src/breakouts/utils.ts +42 -0
  245. package/src/common/errors/no-meeting-info.ts +24 -0
  246. package/src/common/errors/reclaim-host-role-errors.ts +134 -0
  247. package/src/common/errors/webex-errors.ts +44 -2
  248. package/src/common/logs/logger-proxy.ts +1 -1
  249. package/src/common/logs/request.ts +5 -1
  250. package/src/common/queue.ts +22 -8
  251. package/src/config.ts +4 -9
  252. package/src/constants.ts +229 -21
  253. package/src/controls-options-manager/enums.ts +12 -0
  254. package/src/controls-options-manager/index.ts +116 -21
  255. package/src/controls-options-manager/types.ts +59 -0
  256. package/src/controls-options-manager/util.ts +294 -14
  257. package/src/index.ts +44 -0
  258. package/src/interceptors/index.ts +3 -0
  259. package/src/interceptors/locusRetry.ts +67 -0
  260. package/src/interpretation/README.md +60 -0
  261. package/src/interpretation/collection.ts +19 -0
  262. package/src/interpretation/index.ts +349 -0
  263. package/src/interpretation/siLanguage.ts +18 -0
  264. package/src/locus-info/controlsUtils.ts +108 -0
  265. package/src/locus-info/index.ts +417 -59
  266. package/src/locus-info/infoUtils.ts +10 -2
  267. package/src/locus-info/mediaSharesUtils.ts +80 -0
  268. package/src/locus-info/parser.ts +258 -47
  269. package/src/locus-info/selfUtils.ts +81 -5
  270. package/src/media/index.ts +100 -108
  271. package/src/media/properties.ts +88 -117
  272. package/src/mediaQualityMetrics/config.ts +103 -238
  273. package/src/meeting/in-meeting-actions.ts +171 -3
  274. package/src/meeting/index.ts +3411 -2435
  275. package/src/meeting/locusMediaRequest.ts +313 -0
  276. package/src/meeting/muteState.ts +223 -136
  277. package/src/meeting/request.ts +155 -120
  278. package/src/meeting/util.ts +685 -395
  279. package/src/meeting-info/index.ts +81 -8
  280. package/src/meeting-info/meeting-info-v2.ts +170 -14
  281. package/src/meeting-info/util.ts +1 -1
  282. package/src/meeting-info/utilv2.ts +23 -23
  283. package/src/meetings/collection.ts +33 -0
  284. package/src/meetings/index.ts +507 -127
  285. package/src/meetings/meetings.types.ts +12 -0
  286. package/src/meetings/request.ts +2 -0
  287. package/src/meetings/util.ts +81 -12
  288. package/src/member/index.ts +58 -0
  289. package/src/member/types.ts +38 -0
  290. package/src/member/util.ts +141 -25
  291. package/src/members/collection.ts +8 -0
  292. package/src/members/index.ts +134 -8
  293. package/src/members/request.ts +97 -17
  294. package/src/members/types.ts +29 -0
  295. package/src/members/util.ts +333 -240
  296. package/src/metrics/constants.ts +17 -0
  297. package/src/metrics/index.ts +1 -469
  298. package/src/multistream/mediaRequestManager.ts +271 -56
  299. package/src/multistream/receiveSlot.ts +11 -4
  300. package/src/multistream/receiveSlotManager.ts +34 -24
  301. package/src/multistream/remoteMedia.ts +5 -3
  302. package/src/multistream/remoteMediaGroup.ts +78 -0
  303. package/src/multistream/remoteMediaManager.ts +248 -44
  304. package/src/multistream/sendSlotManager.ts +199 -0
  305. package/src/reachability/clusterReachability.ts +320 -0
  306. package/src/reachability/index.ts +229 -346
  307. package/src/reachability/request.ts +8 -4
  308. package/src/reachability/util.ts +24 -0
  309. package/src/reconnection-manager/index.ts +128 -97
  310. package/src/recording-controller/index.ts +20 -3
  311. package/src/recording-controller/util.ts +26 -9
  312. package/src/roap/index.ts +52 -23
  313. package/src/roap/request.ts +48 -67
  314. package/src/roap/turnDiscovery.ts +147 -49
  315. package/src/rtcMetrics/constants.ts +3 -0
  316. package/src/rtcMetrics/index.ts +166 -0
  317. package/src/statsAnalyzer/index.ts +457 -416
  318. package/src/statsAnalyzer/mqaUtil.ts +317 -170
  319. package/src/webinar/collection.ts +31 -0
  320. package/src/webinar/index.ts +62 -0
  321. package/test/integration/spec/converged-space-meetings.js +60 -3
  322. package/test/integration/spec/journey.js +320 -261
  323. package/test/integration/spec/space-meeting.js +76 -3
  324. package/test/unit/spec/annotation/index.ts +418 -0
  325. package/test/unit/spec/breakouts/breakout.ts +118 -28
  326. package/test/unit/spec/breakouts/events.ts +89 -0
  327. package/test/unit/spec/breakouts/index.ts +1349 -114
  328. package/test/unit/spec/breakouts/utils.js +52 -1
  329. package/test/unit/spec/common/queue.js +31 -2
  330. package/test/unit/spec/controls-options-manager/index.js +163 -0
  331. package/test/unit/spec/controls-options-manager/util.js +576 -60
  332. package/test/unit/spec/fixture/locus.js +1 -0
  333. package/test/unit/spec/interceptors/locusRetry.ts +131 -0
  334. package/test/unit/spec/interpretation/collection.ts +15 -0
  335. package/test/unit/spec/interpretation/index.ts +625 -0
  336. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  337. package/test/unit/spec/locus-info/controlsUtils.js +316 -43
  338. package/test/unit/spec/locus-info/index.js +1363 -37
  339. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  340. package/test/unit/spec/locus-info/lib/SeqCmp.json +16 -0
  341. package/test/unit/spec/locus-info/mediaSharesUtils.ts +41 -0
  342. package/test/unit/spec/locus-info/parser.js +116 -35
  343. package/test/unit/spec/locus-info/selfConstant.js +27 -4
  344. package/test/unit/spec/locus-info/selfUtils.js +208 -17
  345. package/test/unit/spec/media/index.ts +173 -81
  346. package/test/unit/spec/media/properties.ts +2 -2
  347. package/test/unit/spec/meeting/in-meeting-actions.ts +85 -3
  348. package/test/unit/spec/meeting/index.js +6821 -2172
  349. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  350. package/test/unit/spec/meeting/muteState.js +402 -212
  351. package/test/unit/spec/meeting/request.js +473 -54
  352. package/test/unit/spec/meeting/utils.js +773 -67
  353. package/test/unit/spec/meeting-info/index.js +300 -0
  354. package/test/unit/spec/meeting-info/meetinginfov2.js +526 -5
  355. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  356. package/test/unit/spec/meetings/collection.js +26 -0
  357. package/test/unit/spec/meetings/index.js +1415 -213
  358. package/test/unit/spec/meetings/utils.js +229 -2
  359. package/test/unit/spec/member/index.js +61 -6
  360. package/test/unit/spec/member/util.js +510 -34
  361. package/test/unit/spec/members/index.js +432 -1
  362. package/test/unit/spec/members/request.js +206 -27
  363. package/test/unit/spec/members/utils.js +210 -0
  364. package/test/unit/spec/metrics/index.js +1 -50
  365. package/test/unit/spec/multistream/mediaRequestManager.ts +781 -114
  366. package/test/unit/spec/multistream/receiveSlot.ts +9 -1
  367. package/test/unit/spec/multistream/receiveSlotManager.ts +32 -30
  368. package/test/unit/spec/multistream/remoteMedia.ts +2 -0
  369. package/test/unit/spec/multistream/remoteMediaGroup.ts +345 -0
  370. package/test/unit/spec/multistream/remoteMediaManager.ts +525 -0
  371. package/test/unit/spec/multistream/sendSlotManager.ts +274 -0
  372. package/test/unit/spec/reachability/clusterReachability.ts +279 -0
  373. package/test/unit/spec/reachability/index.ts +551 -14
  374. package/test/unit/spec/reachability/request.js +3 -1
  375. package/test/unit/spec/reachability/util.ts +40 -0
  376. package/test/unit/spec/reconnection-manager/index.js +171 -11
  377. package/test/unit/spec/recording-controller/index.js +294 -218
  378. package/test/unit/spec/recording-controller/util.js +223 -96
  379. package/test/unit/spec/roap/index.ts +180 -83
  380. package/test/unit/spec/roap/request.ts +100 -62
  381. package/test/unit/spec/roap/turnDiscovery.ts +388 -96
  382. package/test/unit/spec/rtcMetrics/index.ts +122 -0
  383. package/test/unit/spec/stats-analyzer/index.js +1252 -12
  384. package/test/unit/spec/webinar/collection.ts +13 -0
  385. package/test/unit/spec/webinar/index.ts +60 -0
  386. package/test/utils/integrationTestUtils.js +46 -0
  387. package/test/utils/testUtils.js +0 -57
  388. package/test/utils/webex-test-users.js +12 -4
  389. package/dist/metrics/config.js +0 -289
  390. package/dist/metrics/config.js.map +0 -1
  391. package/dist/types/metrics/config.d.ts +0 -169
  392. package/src/index.js +0 -18
  393. package/src/metrics/config.ts +0 -485
@@ -1,13 +1,82 @@
1
1
  import {assert, expect} from '@webex/test-helper-chai';
2
- import Breakout from '@webex/plugin-meetings/src/breakouts/breakout';
3
2
  import Breakouts from '@webex/plugin-meetings/src/breakouts';
4
- import BreakoutCollection from '@webex/plugin-meetings/src/breakouts/collection';
5
3
  import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
6
4
  import {BREAKOUTS} from '@webex/plugin-meetings/src/constants';
7
- import sinon from "sinon";
5
+ import sinon from 'sinon';
8
6
  import MockWebex from '@webex/test-helper-mock-webex';
9
7
  import testUtils from '../../../utils/testUtils';
10
- import BreakoutEditLockedError from "@webex/plugin-meetings/src/breakouts/edit-lock-error";
8
+ import BreakoutEditLockedError from '@webex/plugin-meetings/src/breakouts/edit-lock-error';
9
+ import breakoutEvent from '../../../../src/breakouts/events';
10
+
11
+ const getBOResponse = (status: string) => {
12
+ return {
13
+ url: 'url',
14
+ locusUrl: 'locusUrl',
15
+ mainGroupId: 'mainGroupId',
16
+ mainSessionId: 'mainSessionId',
17
+ groups: [
18
+ {
19
+ id: 'groupId',
20
+ type: 'BREAKOUT',
21
+ status,
22
+ duration: 60000,
23
+ durationSetting: 60000,
24
+ delayCloseTime: 60,
25
+ allowBackToMain: true,
26
+ allowToJoinLater: true,
27
+ startTime: '2023-02-01T23:08:43.200Z',
28
+ sessions: [
29
+ {
30
+ name: 'Breakout Session 1',
31
+ subConfId: 1,
32
+ anyoneCanJoin: false,
33
+ locusUrl: 'locusUrl',
34
+ venueUrl: 'venueUrl',
35
+ allowed: ['allowed id1', 'allowed id2'],
36
+ id: 'sessionId1',
37
+ },
38
+ {
39
+ name: 'Breakout Session 2',
40
+ anyoneCanJoin: true,
41
+ locusUrl: 'locusUrl',
42
+ allowed: ['allowed id3', 'allowed id4'],
43
+ id: 'sessionId2',
44
+ },
45
+ ],
46
+ },
47
+ ],
48
+ };
49
+ };
50
+
51
+ const getBOResponseWithEditLockInfo = (status: string, withOutToken?: boolean) => {
52
+ return {
53
+ url: 'url',
54
+ locusUrl: 'locusUrl',
55
+ mainGroupId: 'mainGroupId',
56
+ mainSessionId: 'mainSessionId',
57
+ editlock: {
58
+ state: 'LOCKED',
59
+ ttl: 30,
60
+ userId: 'cc5d452f-04b6-4876-a4c3-28ca21982c6a',
61
+ token: withOutToken ? '' : 'token1',
62
+ },
63
+ groups: [
64
+ {
65
+ sessions: [
66
+ {
67
+ name: 'Breakout Session 1',
68
+ subConfId: 1,
69
+ anyoneCanJoin: false,
70
+ locusUrl: 'locusUrl',
71
+ venueUrl: 'venueUrl',
72
+ allowed: ['allowed id1', 'allowed id2'],
73
+ id: 'sessionId1',
74
+ },
75
+ ],
76
+ },
77
+ ],
78
+ };
79
+ };
11
80
 
12
81
  describe('plugin-meetings', () => {
13
82
  describe('Breakouts', () => {
@@ -26,6 +95,8 @@ describe('plugin-meetings', () => {
26
95
  breakouts.locusUrl = 'locusUrl';
27
96
  breakouts.breakoutServiceUrl = 'breakoutServiceUrl';
28
97
  webex.request = sinon.stub().returns(Promise.resolve('REQUEST_RETURN_VALUE'));
98
+ webex.meetings = {};
99
+ webex.meetings.getMeetingByType = sinon.stub();
29
100
  });
30
101
 
31
102
  describe('#initialize', () => {
@@ -33,19 +104,59 @@ describe('plugin-meetings', () => {
33
104
  assert.equal(breakouts.namespace, 'Meetings');
34
105
  });
35
106
 
36
- it('emits BREAKOUTS_CLOSING event when the status is CLOSING', () => {
37
- let called = false;
38
- breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.BREAKOUTS_CLOSING, () => {
39
- called = true;
107
+ it('emits BREAKOUTS_CLOSING event when the breakoutStatus is CLOSING', () => {
108
+ const checkIsCalled = (prev, deps) => {
109
+ breakouts.set(prev);
110
+ const breakoutClosingHandler = sinon.stub();
111
+ breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.BREAKOUTS_CLOSING, breakoutClosingHandler);
112
+ assert.notCalled(breakoutClosingHandler);
113
+ breakouts.set(deps);
114
+ assert.calledOnce(breakoutClosingHandler);
115
+ }
116
+
117
+ checkIsCalled({sessionType: BREAKOUTS.SESSION_TYPES.MAIN, groups: undefined, status: undefined}, {
118
+ sessionType: BREAKOUTS.SESSION_TYPES.MAIN,
119
+ groups: [{status: BREAKOUTS.STATUS.CLOSING}],
120
+ status: undefined
40
121
  });
41
122
 
42
- breakouts.set('status', 'something');
123
+ checkIsCalled({sessionType: BREAKOUTS.SESSION_TYPES.MAIN, groups: [{status: BREAKOUTS.STATUS.OPEN}], status: undefined}, {
124
+ sessionType: BREAKOUTS.SESSION_TYPES.MAIN,
125
+ groups: [{status: BREAKOUTS.STATUS.CLOSING}],
126
+ status: undefined
127
+ });
43
128
 
44
- assert.isFalse(called);
129
+ checkIsCalled({sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT, groups: undefined, status: undefined}, {
130
+ sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT,
131
+ groups: undefined,
132
+ status: BREAKOUTS.STATUS.CLOSING
133
+ });
45
134
 
46
- breakouts.set({'status': BREAKOUTS.STATUS.CLOSING});
135
+ checkIsCalled({sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT, groups: undefined, status: BREAKOUTS.STATUS.OPEN}, {
136
+ sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT,
137
+ groups: undefined,
138
+ status: BREAKOUTS.STATUS.CLOSING
139
+ });
47
140
 
48
- assert.isTrue(called);
141
+ });
142
+
143
+ it('should not emits BREAKOUTS_CLOSING event when just sessionType changed from BREAKOUT to MAIN', () => {
144
+ breakouts.set({
145
+ sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT,
146
+ groups: undefined,
147
+ status: BREAKOUTS.STATUS.CLOSING
148
+ });
149
+
150
+ const breakoutClosingHandler = sinon.stub();
151
+ breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.BREAKOUTS_CLOSING, breakoutClosingHandler);
152
+
153
+ breakouts.set({
154
+ sessionType: BREAKOUTS.SESSION_TYPES.MAIN,
155
+ groups: [{status: BREAKOUTS.STATUS.CLOSING}],
156
+ status: undefined
157
+ });
158
+
159
+ assert.notCalled(breakoutClosingHandler);
49
160
  });
50
161
 
51
162
  it('debounces querying rosters on add', () => {
@@ -54,6 +165,64 @@ describe('plugin-meetings', () => {
54
165
 
55
166
  assert.calledOnceWithExactly(breakouts.debouncedQueryRosters);
56
167
  });
168
+
169
+ it('call triggerReturnToMainEvent correctly when requested breakout add', () => {
170
+ breakouts.triggerReturnToMainEvent = sinon.stub();
171
+ breakouts.breakouts.add({sessionId: 'session1', sessionType: 'MAIN'});
172
+ assert.calledOnceWithExactly(breakouts.triggerReturnToMainEvent, breakouts.breakouts.get('session1'));
173
+ });
174
+
175
+ it('call triggerReturnToMainEvent correctly when breakout requestedLastModifiedTime change', () => {
176
+ breakouts.breakouts.add({sessionId: 'session1', sessionType: 'MAIN'});
177
+ breakouts.triggerReturnToMainEvent = sinon.stub();
178
+ breakouts.breakouts.get('session1').set({requestedLastModifiedTime: "2023-05-09T17:16:01.000Z"});
179
+ assert.calledOnceWithExactly(breakouts.triggerReturnToMainEvent, breakouts.breakouts.get('session1'));
180
+ });
181
+
182
+ it('call queryPreAssignments correctly when should query preAssignments is true', () => {
183
+ breakouts.queryPreAssignments = sinon.stub();
184
+ breakouts.set({
185
+ canManageBreakouts: true,
186
+ enableBreakoutSession: true,
187
+ hasBreakoutPreAssignments: true,
188
+ });
189
+ assert.calledThrice(breakouts.queryPreAssignments);
190
+ });
191
+ });
192
+
193
+ describe('#listenToCurrentSessionTypeChange', () => {
194
+ it('triggers leave breakout event when sessionType changed from SESSION to MAIN', () => {
195
+ const handler = sinon.stub();
196
+ breakouts.currentBreakoutSession.set({sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT})
197
+ breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.LEAVE_BREAKOUT, handler);
198
+ breakouts.currentBreakoutSession.set({sessionType: BREAKOUTS.SESSION_TYPES.MAIN});
199
+
200
+ assert.calledOnceWithExactly(handler);
201
+
202
+ breakouts.stopListening(breakouts, BREAKOUTS.EVENTS.LEAVE_BREAKOUT, handler);
203
+ });
204
+
205
+ it('should not triggers leave breakout event when sessionType changed from undefined to MAIN', () => {
206
+ const handler = sinon.stub();
207
+ breakouts.currentBreakoutSession.set({sessionType: undefined})
208
+ breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.LEAVE_BREAKOUT, handler);
209
+ breakouts.currentBreakoutSession.set({sessionType: BREAKOUTS.SESSION_TYPES.MAIN});
210
+
211
+ assert.notCalled(handler);
212
+
213
+ breakouts.stopListening(breakouts, BREAKOUTS.EVENTS.LEAVE_BREAKOUT, handler);
214
+ });
215
+
216
+ it('should not triggers leave breakout event when sessionType changed from MAIN to SESSION', () => {
217
+ const handler = sinon.stub();
218
+ breakouts.currentBreakoutSession.set({sessionType: BREAKOUTS.SESSION_TYPES.MAIN})
219
+ breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.LEAVE_BREAKOUT, handler);
220
+ breakouts.currentBreakoutSession.set({sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT});
221
+
222
+ assert.notCalled(handler);
223
+
224
+ breakouts.stopListening(breakouts, BREAKOUTS.EVENTS.LEAVE_BREAKOUT, handler);
225
+ });
57
226
  });
58
227
 
59
228
  describe('#listenToBroadcastMessages', () => {
@@ -67,7 +236,7 @@ describe('plugin-meetings', () => {
67
236
 
68
237
  breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.MESSAGE, (event) => {
69
238
  message = event;
70
- })
239
+ });
71
240
 
72
241
  breakouts.currentBreakoutSession.sessionId = 'sessionId';
73
242
 
@@ -76,14 +245,14 @@ describe('plugin-meetings', () => {
76
245
  senderUserId: 'senderUserId',
77
246
  sentTime: 'sentTime',
78
247
  message: 'message',
79
- }
248
+ },
80
249
  });
81
250
 
82
251
  assert.deepEqual(message, {
83
- senderUserId: "senderUserId",
252
+ senderUserId: 'senderUserId',
84
253
  sentTime: 'sentTime',
85
254
  message: 'message',
86
- sessionId: 'sessionId'
255
+ sessionId: 'sessionId',
87
256
  });
88
257
  });
89
258
  });
@@ -99,13 +268,13 @@ describe('plugin-meetings', () => {
99
268
 
100
269
  breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.MEMBERS_UPDATE, () => {
101
270
  called = true;
102
- })
271
+ });
103
272
  breakouts.handleRosterUpdate = sinon.stub();
104
273
 
105
274
  callback({
106
275
  data: {
107
- locus: 'locus'
108
- }
276
+ locus: 'locus',
277
+ },
109
278
  });
110
279
 
111
280
  assert.isTrue(called);
@@ -113,6 +282,33 @@ describe('plugin-meetings', () => {
113
282
  });
114
283
  });
115
284
 
285
+ describe('#listenToBreakoutHelp', () => {
286
+ it('triggers ask for help event when a help received', () => {
287
+ const call = webex.internal.mercury.on.getCall(1);
288
+ const callback = call.args[1];
289
+
290
+ assert.equal(call.args[0], 'event:breakout.help');
291
+
292
+ let data;
293
+
294
+ breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.ASK_FOR_HELP, (eventData) => {
295
+ data = eventData;
296
+ });
297
+
298
+ callback({
299
+ data: {
300
+ participant: 'participant',
301
+ sessionId: 'sessionId'
302
+ },
303
+ });
304
+
305
+ assert.deepEqual(data, {
306
+ participant: 'participant',
307
+ sessionId: 'sessionId',
308
+ });
309
+ });
310
+ });
311
+
116
312
  describe('#updateBreakout', () => {
117
313
  it('updates the current breakout session', () => {
118
314
  breakouts.updateBreakout({
@@ -126,7 +322,7 @@ describe('plugin-meetings', () => {
126
322
  enableBreakoutSession: true,
127
323
  startTime: 'startTime',
128
324
  status: 'active',
129
- locusUrl: 'locusUrl'
325
+ locusUrl: 'locusUrl',
130
326
  });
131
327
 
132
328
  assert.equal(breakouts.allowBackToMain, true);
@@ -152,10 +348,90 @@ describe('plugin-meetings', () => {
152
348
  assert.equal(breakouts.currentBreakoutSession.assignedCurrent, false);
153
349
  assert.equal(breakouts.currentBreakoutSession.requested, false);
154
350
  });
351
+
352
+ it('update the startTime correctly when no attribute startTime exists on params', () => {
353
+ breakouts.updateBreakout({
354
+ startTime: "startTime"
355
+ })
356
+ assert.equal(breakouts.startTime, 'startTime');
357
+
358
+ breakouts.updateBreakout({})
359
+ assert.equal(breakouts.startTime, undefined);
360
+ });
361
+
362
+ it('update the status correctly when no attribute status exists on params', () => {
363
+ breakouts.updateBreakout({
364
+ status: 'CLOSING'
365
+ })
366
+ assert.equal(breakouts.status, 'CLOSING');
367
+
368
+ breakouts.updateBreakout({})
369
+ assert.equal(breakouts.status, undefined);
370
+ });
371
+
372
+ it('call clearBreakouts if current breakout is not in-progress', () => {
373
+ breakouts.clearBreakouts = sinon.stub();
374
+ breakouts.updateBreakout({status: 'CLOSED'})
375
+ assert.calledOnce(breakouts.clearBreakouts);
376
+ });
377
+
378
+ it('updates the current breakout session, call onBreakoutJoinResponse when session changed', () => {
379
+ breakouts.webex.meetings = {
380
+ getMeetingByType: sinon.stub().returns({
381
+ id: 'meeting-id'
382
+ })
383
+ };
384
+ breakoutEvent.onBreakoutJoinResponse = sinon.stub();
385
+ breakouts.currentBreakoutSession.sessionId = "sessionId-old";
386
+ breakouts.updateBreakout({
387
+ sessionId: 'sessionId-new',
388
+ groupId: 'groupId',
389
+ sessionType: 'sessionType',
390
+ url: 'url',
391
+ name: 'name',
392
+ allowBackToMain: true,
393
+ delayCloseTime: 10,
394
+ enableBreakoutSession: true,
395
+ startTime: 'startTime',
396
+ status: 'active',
397
+ locusUrl: 'locusUrl',
398
+ breakoutMoveId: 'breakoutMoveId',
399
+ });
400
+
401
+ assert.calledOnce(breakoutEvent.onBreakoutJoinResponse);
402
+
403
+ });
404
+
405
+ it('updates the current breakout session, not call onBreakoutJoinResponse when session no changed', () => {
406
+ breakouts.webex.meetings = {
407
+ getMeetingByType: sinon.stub().returns({
408
+ id: 'meeting-id'
409
+ })
410
+ };
411
+ breakoutEvent.onBreakoutJoinResponse = sinon.stub();
412
+ breakouts.currentBreakoutSession.sessionId = "sessionId";
413
+ breakouts.currentBreakoutSession.groupId = "groupId";
414
+ breakouts.updateBreakout({
415
+ sessionId: 'sessionId',
416
+ groupId: 'groupId',
417
+ sessionType: 'sessionType',
418
+ url: 'url',
419
+ name: 'name',
420
+ allowBackToMain: true,
421
+ delayCloseTime: 10,
422
+ enableBreakoutSession: true,
423
+ startTime: 'startTime',
424
+ status: 'active',
425
+ locusUrl: 'locusUrl',
426
+ breakoutMoveId: 'breakoutMoveId',
427
+ });
428
+
429
+ assert.notCalled(breakoutEvent.onBreakoutJoinResponse);
430
+
431
+ });
155
432
  });
156
433
 
157
434
  describe('#updateBreakoutSessions', () => {
158
-
159
435
  const checkBreakout = (breakout, sessionId, state) => {
160
436
  assert.deepEqual(breakout.attributes, {
161
437
  active: false,
@@ -167,14 +443,13 @@ describe('plugin-meetings', () => {
167
443
  requested: false,
168
444
  url: 'url',
169
445
  sessionId,
170
- ...{[state]: true}
446
+ ...{[state]: true},
171
447
  });
172
- }
448
+ };
173
449
 
174
450
  it('works', () => {
175
-
176
451
  breakouts.set('url', 'url');
177
-
452
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
178
453
  const payload = {
179
454
  breakoutSessions: {
180
455
  active: [{sessionId: 'sessionId1'}],
@@ -182,8 +457,8 @@ describe('plugin-meetings', () => {
182
457
  allowed: [{sessionId: 'sessionId3'}],
183
458
  assignedCurrent: [{sessionId: 'sessionId4'}],
184
459
  requested: [{sessionId: 'sessionId5'}],
185
- }
186
- }
460
+ },
461
+ };
187
462
 
188
463
  breakouts.updateBreakoutSessions(payload);
189
464
 
@@ -192,7 +467,27 @@ describe('plugin-meetings', () => {
192
467
  checkBreakout(breakouts.breakouts.get('sessionId3'), 'sessionId3', 'allowed');
193
468
  checkBreakout(breakouts.breakouts.get('sessionId4'), 'sessionId4', 'assignedCurrent');
194
469
  checkBreakout(breakouts.breakouts.get('sessionId5'), 'sessionId5', 'requested');
195
- })
470
+ });
471
+
472
+ it('set requestedLastModifiedTime correctly', () => {
473
+ const payload = {
474
+ breakoutSessions: {
475
+ assigned: [{sessionId: 'sessionId1'}],
476
+ requested: [{sessionId: 'sessionId2', modifiedAt: "2023-05-09T17:16:01.000Z"}],
477
+ },
478
+ };
479
+
480
+ breakouts.updateBreakoutSessions(payload);
481
+ assert.equal(breakouts.breakouts.get('sessionId1').requestedLastModifiedTime, undefined)
482
+ assert.equal(breakouts.breakouts.get('sessionId2').requestedLastModifiedTime, "2023-05-09T17:16:01.000Z")
483
+ });
484
+
485
+ it('not update breakout sessions when breakouts is closing', () => {
486
+ breakouts.set('status', 'CLOSING');
487
+ breakouts.breakouts.set = sinon.stub();
488
+ breakouts.updateBreakoutSessions({breakoutSessions: {}});
489
+ assert.notCalled(breakouts.breakouts.set);
490
+ });
196
491
  });
197
492
 
198
493
  describe('#locusUrlUpdate', () => {
@@ -203,6 +498,18 @@ describe('plugin-meetings', () => {
203
498
  });
204
499
  });
205
500
 
501
+ describe('#updateCanManageBreakouts', () => {
502
+ it('update canManageBreakouts', () => {
503
+ breakouts.updateCanManageBreakouts(true);
504
+
505
+ assert.equal(breakouts.canManageBreakouts, true);
506
+
507
+ breakouts.updateCanManageBreakouts(false);
508
+
509
+ assert.equal(breakouts.canManageBreakouts, false);
510
+ });
511
+ });
512
+
206
513
  describe('#cleanUp', () => {
207
514
  it('stops listening', () => {
208
515
  breakouts.stopListening = sinon.stub();
@@ -224,26 +531,145 @@ describe('plugin-meetings', () => {
224
531
  const breakout = breakouts.breakouts.models[0];
225
532
  breakout.parseRoster = sinon.stub();
226
533
 
227
- const locus = {controls: {breakout: {sessionId: 'sessionId'}}}
534
+ const locus = {controls: {breakout: {sessionId: 'sessionId'}}};
228
535
 
229
536
  breakouts.handleRosterUpdate(locus);
230
537
  assert.calledOnceWithExactly(breakout.parseRoster, locus);
231
538
  });
232
539
  });
233
540
 
234
- describe('#queryRosters', () => {
541
+ describe('#isActiveBreakout', () => {
542
+ it('return is current is breakout with active status', () => {
543
+ assert.equal(breakouts.isActiveBreakout, false);
544
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.BREAKOUT);
545
+ assert.equal(breakouts.isActiveBreakout, false);
546
+ breakouts.set('status', BREAKOUTS.STATUS.OPEN);
547
+ assert.equal(breakouts.isActiveBreakout, true);
548
+ });
549
+ });
235
550
 
236
- it('makes the expected query', async () => {
551
+ describe('#breakoutGroupId', () => {
552
+ it('return empty breakout group id for managing breakouts if no data', () => {
553
+ assert.equal(breakouts.breakoutGroupId, '');
554
+ breakouts.set('manageGroups', []);
555
+ assert.equal(breakouts.breakoutGroupId, '');
556
+ breakouts.set('manageGroups', [{name: 'test'}]);
557
+ assert.equal(breakouts.breakoutGroupId, undefined);
558
+ });
559
+ it('return the group id if has id in manageGroups', () => {
560
+ breakouts.set('manageGroups', [{id: 'groupId1'}]);
561
+ assert.equal(breakouts.breakoutGroupId, 'groupId1');
562
+ });
563
+ it('return empty group id if group status is CLOSED', () => {
564
+ breakouts.set('manageGroups', [{id: 'groupId1', status: 'CLOSED'}]);
565
+ assert.equal(breakouts.breakoutGroupId, '');
566
+ });
567
+ });
237
568
 
238
- webex.request.returns(Promise.resolve({
239
- body: {
240
- rosters: [{
241
- locus: 'locus1'
242
- }, {
243
- locus: 'locus2'
244
- }]
245
- }
246
- }));
569
+ describe('#shouldQueryPreAssignments', () => {
570
+ it('returns should query preAssignments depends on status', () => {
571
+ assert.equal(breakouts.shouldQueryPreAssignments, false);
572
+ breakouts.set('canManageBreakouts', true);
573
+ assert.equal(breakouts.shouldQueryPreAssignments, false);
574
+ breakouts.set('enableBreakoutSession', true);
575
+ assert.equal(breakouts.shouldQueryPreAssignments, false);
576
+ breakouts.set('hasBreakoutPreAssignments', true);
577
+ assert.equal(breakouts.shouldQueryPreAssignments, true);
578
+ });
579
+ });
580
+
581
+ describe('#breakoutStatus', () => {
582
+ it('return status from groups with session type', () => {
583
+ breakouts.set('groups', [{status: "OPEN"}]);
584
+ breakouts.set('status', "CLOSED");
585
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
586
+
587
+ assert.equal(breakouts.breakoutStatus, "OPEN")
588
+
589
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.BREAKOUT);
590
+
591
+ assert.equal(breakouts.breakoutStatus, "CLOSED")
592
+ });
593
+ });
594
+
595
+ describe('#_setManageGroups', () => {
596
+ it('do nothing if breakout info is empty', () => {
597
+ breakouts._setManageGroups();
598
+ assert.equal(breakouts.manageGroups, undefined);
599
+ breakouts._setManageGroups({body: null});
600
+ assert.equal(breakouts.manageGroups, undefined);
601
+ breakouts._setManageGroups({body: {groups: null}});
602
+ assert.equal(breakouts.manageGroups, undefined);
603
+ });
604
+ it('set the groups into manageGroups if has groups in side breakout info', () => {
605
+ breakouts._setManageGroups({body: {groups: [{id: 'groupId1'}]}});
606
+ assert.deepEqual(breakouts.manageGroups, [{id: 'groupId1'}]);
607
+ });
608
+ });
609
+
610
+ describe('#isBreakoutInProgress', () => {
611
+ it('return breakout is in progress depends on the status(groups/breakouts)', () => {
612
+ breakouts.set('groups', [{status: 'CLOSING'}]);
613
+
614
+ assert.equal(breakouts.isBreakoutInProgress(), true)
615
+
616
+ breakouts.set('groups', undefined);
617
+ breakouts.set('status', 'OPEN');
618
+
619
+ assert.equal(breakouts.isBreakoutInProgress(), true);
620
+
621
+ breakouts.set('groups', [{status: 'CLOSED'}]);
622
+
623
+ assert.equal(breakouts.isBreakoutInProgress(), false);
624
+
625
+ breakouts.set('groups', undefined);
626
+ breakouts.set('status', 'CLOSED');
627
+
628
+ assert.equal(breakouts.isBreakoutInProgress(), false);
629
+
630
+ breakouts.set('status', undefined);
631
+
632
+ assert.equal(breakouts.isBreakoutInProgress(), false);
633
+ });
634
+ });
635
+
636
+ describe('#isBreakoutIClosing', () => {
637
+ it('return breakout is closing depends the status(groups/breakouts)', () => {
638
+ breakouts.set('groups', [{status: 'CLOSING'}]);
639
+
640
+ assert.equal(breakouts.isBreakoutIClosing(), true);
641
+
642
+ breakouts.set('groups', undefined);
643
+ breakouts.set('status', 'CLOSING');
644
+
645
+ assert.equal(breakouts.isBreakoutIClosing(), true);
646
+
647
+ breakouts.set('status', undefined);
648
+
649
+ assert.equal(breakouts.isBreakoutIClosing(), false);
650
+
651
+ breakouts.set('groups', [{status: 'OPEN'}]);
652
+
653
+ assert.equal(breakouts.isBreakoutIClosing(), false);
654
+ });
655
+ });
656
+
657
+ describe('#queryRosters', () => {
658
+ it('makes the expected query', async () => {
659
+ webex.request.returns(
660
+ Promise.resolve({
661
+ body: {
662
+ rosters: [
663
+ {
664
+ locus: 'locus1',
665
+ },
666
+ {
667
+ locus: 'locus2',
668
+ },
669
+ ],
670
+ },
671
+ })
672
+ );
247
673
 
248
674
  breakouts.set('url', 'url');
249
675
  breakouts.set('locusUrl', 'test');
@@ -254,7 +680,7 @@ describe('plugin-meetings', () => {
254
680
 
255
681
  assert.calledOnceWithExactly(webex.request, {
256
682
  uri: 'url/roster',
257
- qs: { locusUrl: 'dGVzdA==' }
683
+ qs: {locusUrl: 'dGVzdA=='},
258
684
  });
259
685
  assert.calledTwice(breakouts.handleRosterUpdate);
260
686
 
@@ -263,7 +689,7 @@ describe('plugin-meetings', () => {
263
689
  });
264
690
 
265
691
  it('logs the error if the query fails', async () => {
266
- const error = new Error('something went wrong')
692
+ const error = new Error('something went wrong');
267
693
  webex.request.rejects(error);
268
694
  LoggerProxy.logger.error = sinon.stub();
269
695
 
@@ -290,11 +716,27 @@ describe('plugin-meetings', () => {
290
716
  describe('isInMainSession', () => {
291
717
  it('returns true when sessionType is MAIN', () => {
292
718
  assert.equal(breakouts.isInMainSession, false);
293
- breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN)
719
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
294
720
  assert.equal(breakouts.isInMainSession, true);
295
721
  });
296
722
  });
297
723
 
724
+ describe('#clearBreakouts', () => {
725
+ it('call reset to clear breakouts', () => {
726
+ breakouts.set('breakouts', [{id: 'session1'}]);
727
+ breakouts.breakouts.reset = sinon.stub();
728
+ breakouts.clearBreakouts();
729
+ assert.calledWith(breakouts.breakouts.reset);
730
+ });
731
+
732
+ it('do nothing if breakouts already is empty', () => {
733
+ breakouts.set('breakouts', []);
734
+ breakouts.breakouts.reset = sinon.stub();
735
+ breakouts.clearBreakouts();
736
+ assert.notCalled(breakouts.breakouts.reset);
737
+ });
738
+ });
739
+
298
740
  describe('#getMainSession', () => {
299
741
  it('returns main session as expect', () => {
300
742
  breakouts.updateBreakout({
@@ -308,24 +750,24 @@ describe('plugin-meetings', () => {
308
750
  enableBreakoutSession: true,
309
751
  startTime: 'startTime',
310
752
  status: 'active',
311
- locusUrl: 'locusUrl'
753
+ locusUrl: 'locusUrl',
312
754
  });
313
755
  const payload = {
314
756
  breakoutSessions: {
315
757
  active: [{sessionId: 'sessionId1'}],
316
- }
317
- }
318
- breakouts.updateBreakoutSessions(payload);
758
+ },
759
+ };
319
760
 
320
761
  breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
762
+ breakouts.updateBreakoutSessions(payload);
321
763
  let result = breakouts.getMainSession();
322
764
  assert.equal(result.sessionId, 'sessionId');
323
765
 
324
766
  const payload2 = {
325
767
  breakoutSessions: {
326
768
  active: [{sessionId: 'sessionId1', sessionType: BREAKOUTS.SESSION_TYPES.MAIN}],
327
- }
328
- }
769
+ },
770
+ };
329
771
  breakouts.updateBreakoutSessions(payload2);
330
772
  breakouts.set('sessionType', 'BREAKOUT');
331
773
  result = breakouts.getMainSession();
@@ -334,12 +776,12 @@ describe('plugin-meetings', () => {
334
776
  it('throw error if cannot find main session', () => {
335
777
  const fn = () => {
336
778
  breakouts.getMainSession();
337
- }
779
+ };
338
780
  expect(fn).to.throw(/no main session found/);
339
781
  });
340
782
  });
341
783
 
342
- describe('#askAllToReturn', () => {
784
+ describe('#askAllToReturn', () => {
343
785
  it('makes the request as expected', async () => {
344
786
  breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
345
787
  breakouts.currentBreakoutSession.sessionId = 'sessionId';
@@ -350,8 +792,8 @@ describe('plugin-meetings', () => {
350
792
  uri: 'url/requestMove',
351
793
  body: {
352
794
  groupId: 'groupId',
353
- sessionId: 'sessionId'
354
- }
795
+ sessionId: 'sessionId',
796
+ },
355
797
  });
356
798
  });
357
799
  });
@@ -364,16 +806,19 @@ describe('plugin-meetings', () => {
364
806
  });
365
807
 
366
808
  describe('#toggleBreakout', () => {
367
- it('enableBreakoutSession is undefined, run enableBreakouts then toggleBreakout', async() => {
368
- breakouts.enableBreakoutSession = undefined;
369
- breakouts.enableBreakouts = sinon.stub().resolves(({body: {
809
+ const mockEnableResponse = {
810
+ body: {
370
811
  sessionId: 'sessionId',
371
812
  groupId: 'groupId',
372
813
  name: 'name',
373
814
  current: true,
374
815
  sessionType: 'sessionType',
375
- url: 'url'
376
- }}))
816
+ url: 'url',
817
+ },
818
+ };
819
+ it('enableBreakoutSession is undefined, run enableBreakouts then toggleBreakout', async () => {
820
+ breakouts.enableBreakoutSession = undefined;
821
+ breakouts.enableBreakouts = sinon.stub().resolves(mockEnableResponse);
377
822
  breakouts.updateBreakout = sinon.stub().resolves();
378
823
  breakouts.doToggleBreakout = sinon.stub().resolves();
379
824
 
@@ -385,12 +830,31 @@ describe('plugin-meetings', () => {
385
830
  name: 'name',
386
831
  current: true,
387
832
  sessionType: 'sessionType',
388
- url: 'url'
833
+ url: 'url',
389
834
  });
390
835
  assert.calledOnceWithExactly(breakouts.doToggleBreakout, false);
391
836
  });
392
837
 
393
- it('enableBreakoutSession is exist, run toggleBreakout', async() => {
838
+ it('first time enable breakouts, call updateBreakout to set initial params', async () => {
839
+ breakouts.enableBreakoutSession = undefined;
840
+ breakouts.enableBreakouts = sinon.stub().resolves(mockEnableResponse);
841
+ breakouts.updateBreakout = sinon.stub().resolves();
842
+ breakouts.doToggleBreakout = sinon.stub();
843
+
844
+ await breakouts.toggleBreakout(true);
845
+ assert.calledOnceWithExactly(breakouts.updateBreakout, {
846
+ sessionId: 'sessionId',
847
+ groupId: 'groupId',
848
+ name: 'name',
849
+ current: true,
850
+ sessionType: 'sessionType',
851
+ url: 'url',
852
+ });
853
+
854
+ assert.notCalled(breakouts.doToggleBreakout);
855
+ });
856
+
857
+ it('enableBreakoutSession is exist, run toggleBreakout', async () => {
394
858
  breakouts.enableBreakoutSession = true;
395
859
  breakouts.doToggleBreakout = sinon.stub().resolves();
396
860
  await breakouts.toggleBreakout(true);
@@ -406,23 +870,31 @@ describe('plugin-meetings', () => {
406
870
  method: 'POST',
407
871
  uri: 'breakoutServiceUrl',
408
872
  body: {
409
- locusUrl: 'locusUrl'
410
- }
873
+ locusUrl: 'locusUrl',
874
+ },
411
875
  });
412
876
 
413
877
  assert.equal(result, 'REQUEST_RETURN_VALUE');
414
878
  });
415
879
  });
416
880
 
417
- describe('#broadcast', () => {
881
+ describe('#broadcast', () => {
418
882
  it('makes the request as expected', async () => {
419
- breakouts.breakoutRequest.broadcast = sinon.stub().returns(Promise.resolve('REQUEST_RETURN_VALUE'));
883
+ breakouts.breakoutRequest.broadcast = sinon
884
+ .stub()
885
+ .returns(Promise.resolve('REQUEST_RETURN_VALUE'));
886
+ webex.request.returns(
887
+ Promise.resolve({
888
+ body: getBOResponse('OPEN'),
889
+ })
890
+ );
891
+ await breakouts.getBreakout();
420
892
  let result = await breakouts.broadcast('hello');
421
893
  assert.calledWithExactly(breakouts.breakoutRequest.broadcast, {
422
894
  url: 'url',
423
895
  message: 'hello',
424
896
  options: undefined,
425
- groupId: 'groupId'
897
+ groupId: 'groupId',
426
898
  });
427
899
 
428
900
  assert.equal(result, 'REQUEST_RETURN_VALUE');
@@ -432,29 +904,343 @@ describe('plugin-meetings', () => {
432
904
  url: 'url',
433
905
  groupId: 'groupId',
434
906
  message: 'hello',
435
- options: {presenters: true, cohosts: true}
907
+ options: {presenters: true, cohosts: true},
436
908
  });
437
- assert.equal(result, 'REQUEST_RETURN_VALUE')
909
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
438
910
  });
439
911
 
440
912
  it('throw error if no breakout group id found', () => {
441
913
  breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
442
914
  const fn = () => {
443
915
  breakouts.broadcast('hello');
444
- }
916
+ };
445
917
  expect(fn).to.throw(/no breakout session found/);
446
918
  });
447
919
  });
448
920
 
921
+ describe('#update', () => {
922
+ it('makes the request as expected', async () => {
923
+ const mockedReturnBody = getBOResponse('OPEN');
924
+ webex.request.returns(
925
+ Promise.resolve({
926
+ body: mockedReturnBody,
927
+ })
928
+ );
929
+ breakouts.editLock = {
930
+ token: 'token1',
931
+ };
932
+ const params = {
933
+ id: 'groupId',
934
+ sessions: [{name: 'Session 1'}],
935
+ };
936
+ breakouts._setManageGroups = sinon.stub();
937
+ const result = await breakouts.update(params);
938
+ assert.calledOnceWithExactly(webex.request, {
939
+ method: 'PUT',
940
+ uri: 'url',
941
+ body: {
942
+ editlock: {token: 'token1', refresh: true},
943
+ groups: [params],
944
+ },
945
+ });
946
+ assert.calledOnceWithExactly(breakouts._setManageGroups, {
947
+ body: mockedReturnBody,
948
+ });
949
+ });
950
+ it('makes the request as expected when unlockEdit is true', async () => {
951
+ breakouts.editLock = {
952
+ token: 'token1',
953
+ };
954
+ const params = {
955
+ id: 'groupId',
956
+ sessions: [{name: 'Session 1'}],
957
+ };
958
+ breakouts._clearEditLockInfo = sinon.stub();
959
+ const result = await breakouts.update(params, true);
960
+ assert.calledOnceWithExactly(webex.request, {
961
+ method: 'PUT',
962
+ uri: 'url',
963
+ body: {
964
+ editlock: {token: 'token1', refresh: false},
965
+ groups: [params],
966
+ },
967
+ });
968
+ assert.calledOnceWithExactly(breakouts._clearEditLockInfo);
969
+ });
970
+ it('throw error if missing id in params', async () => {
971
+ const params = {
972
+ sessions: [{name: 'Session 1'}],
973
+ };
974
+ await breakouts.update(params).catch((error) => {
975
+ assert.equal(error.toString(), 'Error: Missing breakout group id');
976
+ });
977
+ });
978
+ it('rejects when edit lock token mismatch', async () => {
979
+ webex.request.returns(
980
+ Promise.reject({
981
+ body: {
982
+ errorCode: BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
983
+ message: 'Edit lock token mismatch',
984
+ },
985
+ })
986
+ );
987
+ LoggerProxy.logger.info = sinon.stub();
988
+
989
+ const params = {
990
+ id: 'groupId',
991
+ sessions: [{name: 'Session 1'}],
992
+ };
993
+
994
+ await assert.isRejected(
995
+ breakouts.update(params),
996
+ BreakoutEditLockedError,
997
+ 'Edit lock token mismatch'
998
+ );
999
+ assert.calledOnceWithExactly(
1000
+ LoggerProxy.logger.info,
1001
+ 'Breakouts#update --> Edit lock token mismatch'
1002
+ );
1003
+ });
1004
+
1005
+ it('rejects when edit not authorized', async () => {
1006
+ webex.request.returns(
1007
+ Promise.reject({
1008
+ body: {
1009
+ errorCode: BREAKOUTS.ERROR_CODE.EDIT_NOT_AUTHORIZED,
1010
+ },
1011
+ })
1012
+ );
1013
+ LoggerProxy.logger.info = sinon.stub();
1014
+
1015
+ const params = {
1016
+ id: 'groupId',
1017
+ sessions: [{name: 'Session 1'}],
1018
+ };
1019
+
1020
+ await assert.isRejected(
1021
+ breakouts.update(params),
1022
+ BreakoutEditLockedError,
1023
+ 'Not authorized to interact with edit lock'
1024
+ );
1025
+
1026
+ assert.calledOnceWithExactly(
1027
+ LoggerProxy.logger.info,
1028
+ 'Breakouts#update --> Not authorized to interact with edit lock'
1029
+ );
1030
+ });
1031
+
1032
+ it('rejects when other unknow error', async () => {
1033
+ const mockError = new Error('something wrong');
1034
+ webex.request.returns(Promise.reject(mockError));
1035
+ LoggerProxy.logger.info = sinon.stub();
1036
+
1037
+ const params = {
1038
+ id: 'groupId',
1039
+ sessions: [{name: 'Session 1'}],
1040
+ };
1041
+
1042
+ await assert.isRejected(breakouts.update(params), mockError, 'something wrong');
1043
+
1044
+ assert.calledOnceWithExactly(
1045
+ LoggerProxy.logger.info,
1046
+ 'Breakouts#update --> something wrong'
1047
+ );
1048
+ });
1049
+ });
1050
+
1051
+ describe('#start', () => {
1052
+ it('should start breakout sessions', async () => {
1053
+ const mockedReturnBody = getBOResponse('OPEN');
1054
+ webex.request.returns(
1055
+ Promise.resolve({
1056
+ body: mockedReturnBody,
1057
+ })
1058
+ );
1059
+
1060
+ breakouts.set('url', 'url');
1061
+ await breakouts.getBreakout();
1062
+
1063
+ const result = await breakouts.start();
1064
+ breakouts._setManageGroups = sinon.stub();
1065
+ await breakouts.start({id: 'id', someOtherParam: 'someOtherParam'});
1066
+
1067
+ const arg = webex.request.getCall(1).args[0];
1068
+ const argObj1 = arg.body.groups[0];
1069
+ const argObj2 = webex.request.getCall(2).args[0].body.groups[0];
1070
+
1071
+ assert.equal(arg.uri, 'url');
1072
+ assert.equal(arg.method, 'PUT');
1073
+ assert.deepEqual(argObj1, {
1074
+ id: 'groupId',
1075
+ action: 'START',
1076
+ allowBackToMain: false,
1077
+ allowToJoinLater: false,
1078
+ });
1079
+ assert.deepEqual(argObj2, {
1080
+ id: 'id',
1081
+ action: 'START',
1082
+ allowBackToMain: false,
1083
+ allowToJoinLater: false,
1084
+ someOtherParam: 'someOtherParam',
1085
+ });
1086
+ assert.deepEqual(result, {body: mockedReturnBody});
1087
+ assert.calledWithExactly(breakouts._setManageGroups, {body: mockedReturnBody})
1088
+ });
1089
+
1090
+ it('rejects when edit lock token mismatch', async () => {
1091
+ webex.request.returns(
1092
+ Promise.reject({
1093
+ body: {
1094
+ errorCode: BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
1095
+ message: 'Edit lock token mismatch',
1096
+ },
1097
+ })
1098
+ );
1099
+
1100
+ await assert.isRejected(
1101
+ breakouts.start(),
1102
+ BreakoutEditLockedError,
1103
+ 'Edit lock token mismatch'
1104
+ );
1105
+ });
1106
+ });
1107
+
1108
+ describe('#end', () => {
1109
+ it('should end breakout sessions', async () => {
1110
+ webex.request.returns(
1111
+ Promise.resolve({
1112
+ body: getBOResponse('CLOSING'),
1113
+ })
1114
+ );
1115
+
1116
+ breakouts.set('url', 'url');
1117
+ breakouts.set('delayCloseTime', 60);
1118
+ await breakouts.getBreakout();
1119
+
1120
+ const result = await breakouts.end();
1121
+
1122
+ breakouts._setManageGroups = sinon.stub();
1123
+ await breakouts.end({id: 'id', someOtherParam: 'someOtherParam'});
1124
+ const arg = webex.request.getCall(1).args[0];
1125
+ const argObj1 = arg.body.groups[0];
1126
+ const argObj2 = webex.request.getCall(2).args[0].body.groups[0];
1127
+
1128
+ assert.equal(arg.uri, 'url');
1129
+ assert.equal(arg.method, 'PUT');
1130
+ assert.deepEqual(argObj1, {id: 'groupId', action: 'CLOSE', delayCloseTime: 60});
1131
+ assert.deepEqual(argObj2, {
1132
+ id: 'id',
1133
+ action: 'CLOSE',
1134
+ delayCloseTime: 60,
1135
+ someOtherParam: 'someOtherParam',
1136
+ });
1137
+ assert.deepEqual(result, {body: getBOResponse('CLOSING')});
1138
+ assert.calledOnceWithExactly(breakouts._setManageGroups, {body: getBOResponse('CLOSING')});
1139
+ });
1140
+
1141
+ it('rejects when edit lock token mismatch', async () => {
1142
+ webex.request.returns(
1143
+ Promise.reject({
1144
+ body: {
1145
+ errorCode: BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
1146
+ message: 'Edit lock token mismatch',
1147
+ },
1148
+ })
1149
+ );
1150
+
1151
+ await assert.isRejected(
1152
+ breakouts.end(),
1153
+ BreakoutEditLockedError,
1154
+ 'Edit lock token mismatch'
1155
+ );
1156
+ });
1157
+ });
1158
+
1159
+ describe('#getBreakout', () => {
1160
+ it('should get breakout sessions', async () => {
1161
+ webex.request.returns(
1162
+ Promise.resolve({
1163
+ body: getBOResponse('PENDING'),
1164
+ })
1165
+ );
1166
+
1167
+ breakouts.set('url', 'url');
1168
+ const result = await breakouts.getBreakout();
1169
+
1170
+ breakouts._setManageGroups = sinon.stub();
1171
+ await breakouts.getBreakout(true);
1172
+ const arg1 = webex.request.getCall(0).args[0];
1173
+ const arg2 = webex.request.getCall(1).args[0];
1174
+
1175
+ assert.equal(arg1.uri, 'url');
1176
+ assert.equal(arg2.uri, 'url?editlock=true');
1177
+ assert.equal(arg1.method, 'GET');
1178
+ assert.deepEqual(result, {body: getBOResponse('PENDING')});
1179
+ assert.deepEqual(breakouts.manageGroups, result.body.groups);
1180
+ assert.equal(breakouts.breakoutGroupId, 'groupId');
1181
+ assert.calledOnceWithExactly(breakouts._setManageGroups, {body: getBOResponse('PENDING')});
1182
+ });
1183
+
1184
+ it('breakoutGroupId should be empty if it is CLOSED group', async () => {
1185
+ webex.request.returns(
1186
+ Promise.resolve({
1187
+ body: getBOResponse('CLOSED'),
1188
+ })
1189
+ );
1190
+
1191
+ breakouts.set('url', 'url');
1192
+ await breakouts.getBreakout();
1193
+
1194
+ assert.equal(breakouts.breakoutGroupId, '');
1195
+ });
1196
+
1197
+ it('should call keep alive when response with edit lock info', async () => {
1198
+ webex.request.returns(
1199
+ Promise.resolve({
1200
+ body: getBOResponseWithEditLockInfo('PENDING'),
1201
+ })
1202
+ );
1203
+
1204
+ breakouts.set('url', 'url');
1205
+ breakouts.keepEditLockAlive = sinon.stub().resolves();
1206
+ const result = await breakouts.getBreakout();
1207
+ await breakouts.getBreakout(true);
1208
+
1209
+ assert.calledOnceWithExactly(breakouts.keepEditLockAlive);
1210
+ });
1211
+ it('not call keep alive when response with no edit lock token', async () => {
1212
+ webex.request.returns(
1213
+ Promise.resolve({
1214
+ body: getBOResponseWithEditLockInfo('PENDING', true),
1215
+ })
1216
+ );
1217
+
1218
+ breakouts.set('url', 'url');
1219
+ breakouts.keepEditLockAlive = sinon.stub().resolves();
1220
+ const result = await breakouts.getBreakout();
1221
+ await breakouts.getBreakout(true);
1222
+
1223
+ assert.notCalled(breakouts.keepEditLockAlive);
1224
+ });
1225
+ });
1226
+
449
1227
  describe('doToggleBreakout', () => {
450
1228
  it('makes the request as expected', async () => {
1229
+ breakouts.set('editLock', {
1230
+ ttl: 30,
1231
+ token: 'editToken',
1232
+ state: 'UNLOCKED',
1233
+ });
451
1234
  const result = await breakouts.doToggleBreakout(true);
452
1235
  assert.calledOnceWithExactly(webex.request, {
453
1236
  method: 'PUT',
454
1237
  uri: 'url',
455
1238
  body: {
456
- enableBreakoutSession: true
457
- }
1239
+ editlock: {
1240
+ token: 'editToken',
1241
+ },
1242
+ enableBreakoutSession: true,
1243
+ },
458
1244
  });
459
1245
 
460
1246
  assert.equal(result, 'REQUEST_RETURN_VALUE');
@@ -463,16 +1249,21 @@ describe('plugin-meetings', () => {
463
1249
 
464
1250
  describe('delete', () => {
465
1251
  it('makes the request as expected', async () => {
466
- webex.request.returns(Promise.resolve({
467
- body: {
468
- groups: [{
469
- id : "455556a4-37cd-4baa-89bc-8730581a1cc0",
470
- status : "CLOSE",
471
- type : "BREAKOUT",
472
- }]
473
- }
474
- }));
1252
+ webex.request.returns(
1253
+ Promise.resolve({
1254
+ body: {
1255
+ groups: [
1256
+ {
1257
+ id: '455556a4-37cd-4baa-89bc-8730581a1cc0',
1258
+ status: 'CLOSE',
1259
+ type: 'BREAKOUT',
1260
+ },
1261
+ ],
1262
+ },
1263
+ })
1264
+ );
475
1265
 
1266
+ breakouts._setManageGroups = sinon.stub();
476
1267
  const result = await breakouts.clearSessions();
477
1268
  assert.calledOnceWithExactly(webex.request, {
478
1269
  method: 'PUT',
@@ -480,76 +1271,520 @@ describe('plugin-meetings', () => {
480
1271
  body: {
481
1272
  groups: [
482
1273
  {
483
- action: BREAKOUTS.ACTION_TYPES.DELETE,
1274
+ action: BREAKOUTS.ACTION.DELETE,
484
1275
  },
485
1276
  ],
486
- }
1277
+ },
487
1278
  });
488
1279
 
489
- assert.equal(breakouts.groups[0].status, "CLOSE")
1280
+ assert.calledOnceWithExactly(breakouts._setManageGroups, {
1281
+ body: {
1282
+ groups: [
1283
+ {
1284
+ id: '455556a4-37cd-4baa-89bc-8730581a1cc0',
1285
+ status: 'CLOSE',
1286
+ type: 'BREAKOUT',
1287
+ },
1288
+ ],
1289
+ },
1290
+ });
490
1291
  });
491
1292
 
492
1293
  it('rejects when edit lock token mismatch', async () => {
493
- webex.request.returns(Promise.reject({
494
- body: {
495
- "errorCode":BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
496
- "message":"Edit lock token mismatch"
497
- }
498
- }));
1294
+ webex.request.returns(
1295
+ Promise.reject({
1296
+ body: {
1297
+ errorCode: BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
1298
+ message: 'Edit lock token mismatch',
1299
+ },
1300
+ })
1301
+ );
499
1302
 
500
1303
  await assert.isRejected(
501
1304
  breakouts.clearSessions(),
502
1305
  BreakoutEditLockedError,
503
1306
  'Edit lock token mismatch'
504
1307
  );
505
-
506
1308
  });
507
1309
  });
508
1310
 
509
1311
  describe('create', () => {
510
1312
  it('response not include groups info', async () => {
511
- const sessions = [{'name':'session1', "anyoneCanJoin" : true}];
512
- const result = await breakouts.create(sessions);
1313
+ const sessions = [{name: 'session1', anyoneCanJoin: true}];
1314
+ const result = await breakouts.create({sessions});
513
1315
 
514
1316
  assert.equal(result, 'REQUEST_RETURN_VALUE');
515
-
516
1317
  });
517
1318
 
518
1319
  it('response include groups info', async () => {
519
- const sessions = [{'name':'session1', "anyoneCanJoin" : true}];
1320
+ const sessions = [{name: 'session1', anyoneCanJoin: true}];
1321
+
1322
+ webex.request.returns(
1323
+ Promise.resolve({
1324
+ body: {
1325
+ groups: [
1326
+ {
1327
+ id: '455556a4-37cd-4baa-89bc-8730581a1cc0',
1328
+ status: 'PENDING',
1329
+ type: 'BREAKOUT',
1330
+ },
1331
+ ],
1332
+ },
1333
+ })
1334
+ );
520
1335
 
521
- webex.request.returns(Promise.resolve({
522
- body: {
523
- groups: [{
524
- id : "455556a4-37cd-4baa-89bc-8730581a1cc0",
525
- status : "PENDING",
526
- type : "BREAKOUT",
527
- }]
528
- }
529
- }));
1336
+ const result = await breakouts.create({sessions});
1337
+
1338
+ assert.equal(breakouts.manageGroups[0].id, '455556a4-37cd-4baa-89bc-8730581a1cc0');
1339
+ });
530
1340
 
531
- const result = await breakouts.create(sessions);
1341
+ it('rejects when edit lock token mismatch', async () => {
1342
+ const sessions = [{name: 'session1', anyoneCanJoin: true}];
1343
+
1344
+ webex.request.returns(
1345
+ Promise.reject({
1346
+ body: {
1347
+ errorCode: BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
1348
+ message: 'Edit lock token mismatch',
1349
+ },
1350
+ })
1351
+ );
1352
+
1353
+ await assert.isRejected(
1354
+ breakouts.create({sessions}),
1355
+ BreakoutEditLockedError,
1356
+ 'Edit lock token mismatch'
1357
+ );
1358
+ });
1359
+ });
1360
+
1361
+ describe('enableAndLockBreakout', () => {
1362
+ it('enableBreakoutSession is true', async () => {
1363
+ breakouts.enableBreakoutSession = true;
1364
+
1365
+ breakouts.lockBreakout = sinon.stub().resolves();
532
1366
 
533
- assert.equal(breakouts.groups[0].id, "455556a4-37cd-4baa-89bc-8730581a1cc0")
1367
+ breakouts.enableAndLockBreakout();
534
1368
 
1369
+ assert.calledOnceWithExactly(breakouts.lockBreakout);
535
1370
  });
536
1371
 
537
- it('rejects when edit lock token mismatch', async () => {
538
- const sessions = [{'name':'session1', "anyoneCanJoin" : true}];
1372
+ it('enableBreakoutSession is false', async () => {
1373
+ breakouts.enableBreakoutSession = false;
1374
+
1375
+ breakouts.enableBreakouts = sinon.stub().resolves();
1376
+
1377
+ breakouts.enableAndLockBreakout();
1378
+
1379
+ assert.calledOnceWithExactly(breakouts.enableBreakouts);
1380
+ });
1381
+ });
1382
+
1383
+ describe('hasBreakoutLocked', () => {
1384
+ it('has breakout locked is true', async () => {
1385
+ breakouts.editLock = {
1386
+ ttl: 30,
1387
+ token: 'token',
1388
+ state: 'LOCKED',
1389
+ };
1390
+
1391
+ assert.equal(breakouts.hasBreakoutLocked(), true);
1392
+ });
1393
+
1394
+ it('breakout locked by others', async () => {
1395
+ breakouts.editLock = {
1396
+ ttl: 30,
1397
+ token: '',
1398
+ state: 'LOCKED',
1399
+ };
1400
+
1401
+ assert.equal(breakouts.hasBreakoutLocked(), false);
1402
+ });
1403
+
1404
+ it('breakout not locked', async () => {
1405
+ breakouts.editLock = {
1406
+ ttl: 30,
1407
+ token: '',
1408
+ state: 'UNLOCKED',
1409
+ };
1410
+
1411
+ assert.equal(breakouts.hasBreakoutLocked(), false);
1412
+ });
1413
+ });
1414
+
1415
+ describe('lockBreakout', () => {
1416
+ it('lock breakout is true', async () => {
1417
+ breakouts.editLock = {
1418
+ ttl: 30,
1419
+ token: 'token',
1420
+ state: 'UNLOCKED',
1421
+ };
1422
+
1423
+ breakouts.keepEditLockAlive = sinon.stub().resolves();
539
1424
 
540
- webex.request.returns(Promise.reject({
1425
+ breakouts.lockBreakout();
1426
+
1427
+ assert.calledOnceWithExactly(breakouts.keepEditLockAlive);
1428
+ });
1429
+
1430
+ it('lock breakout throw error', async () => {
1431
+ breakouts.editLock = {
1432
+ ttl: 30,
1433
+ token: '2ad57140-01b5-4bd0-a5a7-4dccdc06904c',
1434
+ state: 'LOCKED',
1435
+ };
1436
+
1437
+ await expect(breakouts.lockBreakout()).to.be.rejectedWith('Breakout already locked');
1438
+ });
1439
+
1440
+ it('lock breakout without editLock', async () => {
1441
+ breakouts.getBreakout = sinon.stub().resolves();
1442
+
1443
+ breakouts.lockBreakout();
1444
+
1445
+ assert.calledOnceWithExactly(breakouts.getBreakout, true);
1446
+ });
1447
+ });
1448
+
1449
+ describe('unLockEditBreakout', () => {
1450
+ it('unLock edit breakout request as expected', async () => {
1451
+ breakouts.set('editLock', {
1452
+ ttl: 30,
1453
+ token: '2ad57140-01b5-4bd0-a5a7-4dccdc06904c',
1454
+ state: 'LOCKED',
1455
+ });
1456
+
1457
+ breakouts.unLockEditBreakout();
1458
+ assert.calledOnceWithExactly(webex.request, {
1459
+ method: 'DELETE',
1460
+ uri: 'url/editlock/2ad57140-01b5-4bd0-a5a7-4dccdc06904c',
1461
+ });
1462
+ });
1463
+
1464
+ it('do not call unLock if edit lock info not exist ', async () => {
1465
+ breakouts.unLockEditBreakout();
1466
+ assert.notCalled(webex.request);
1467
+ });
1468
+ });
1469
+
1470
+ describe('keepEditLockAlive', () => {
1471
+ it('keep edit lock', () => {
1472
+ const clock = sinon.useFakeTimers();
1473
+
1474
+ breakouts.set('editLock', {
1475
+ ttl: 30,
1476
+ token: 'token',
1477
+ state: 'UNLOCKED',
1478
+ });
1479
+
1480
+ breakouts.keepEditLockAlive();
1481
+ clock.tick(15001);
1482
+
1483
+ assert.calledOnceWithExactly(webex.request, {
1484
+ method: 'PUT',
1485
+ uri: 'url/editlock/token',
1486
+ });
1487
+
1488
+ clock.restore();
1489
+ });
1490
+
1491
+ it('keep edit lock, ttl < 30, also using 30', () => {
1492
+ const clock = sinon.useFakeTimers();
1493
+
1494
+ breakouts.set('editLock', {
1495
+ ttl: 20,
1496
+ token: 'token',
1497
+ state: 'UNLOCKED',
1498
+ });
1499
+
1500
+ breakouts.keepEditLockAlive();
1501
+ clock.tick(15001);
1502
+
1503
+ assert.calledOnceWithExactly(webex.request, {
1504
+ method: 'PUT',
1505
+ uri: 'url/editlock/token',
1506
+ });
1507
+
1508
+ clock.restore();
1509
+ });
1510
+
1511
+ it('keep edit lock, ttl > 30, using the ttl', () => {
1512
+ const clock = sinon.useFakeTimers();
1513
+
1514
+ breakouts.set('editLock', {
1515
+ ttl: 50,
1516
+ token: 'token',
1517
+ state: 'UNLOCKED',
1518
+ });
1519
+
1520
+ breakouts.keepEditLockAlive();
1521
+ clock.tick(24099);
1522
+
1523
+ assert.notCalled(webex.request);
1524
+
1525
+ clock.restore();
1526
+ });
1527
+
1528
+ it('keep edit lock, throw error, clearInterval', async () => {
1529
+ breakouts._clearEditLockInfo = sinon.stub();
1530
+
1531
+ const error = new Error('something went wrong');
1532
+ webex.request.rejects(error);
1533
+
1534
+ const clock = sinon.useFakeTimers();
1535
+
1536
+ breakouts.set('editLock', {
1537
+ ttl: 30,
1538
+ token: 'token',
1539
+ state: 'UNLOCKED',
1540
+ });
1541
+
1542
+ breakouts.keepEditLockAlive();
1543
+ clock.tick(15001);
1544
+
1545
+ await testUtils.flushPromises();
1546
+
1547
+ assert.calledOnceWithExactly(breakouts._clearEditLockInfo);
1548
+
1549
+ clock.restore();
1550
+ });
1551
+
1552
+ it('keep edit lock, do not call until reached ttl', () => {
1553
+ const clock = sinon.useFakeTimers();
1554
+
1555
+ breakouts.set('editLock', {
1556
+ ttl: 30,
1557
+ token: 'token',
1558
+ state: 'UNLOCKED',
1559
+ });
1560
+
1561
+ breakouts.keepEditLockAlive();
1562
+ clock.tick(14999);
1563
+
1564
+ assert.notCalled(webex.request);
1565
+
1566
+ clock.tick(1);
1567
+ assert.calledOnceWithExactly(webex.request, {
1568
+ method: 'PUT',
1569
+ uri: 'url/editlock/token',
1570
+ });
1571
+
1572
+ clock.restore();
1573
+ });
1574
+
1575
+ it('clear interval first if previous one is in work', () => {
1576
+ breakouts.set('editLock', {
1577
+ ttl: 30,
1578
+ token: 'token',
1579
+ });
1580
+ const clock = sinon.useFakeTimers();
1581
+ window.clearInterval = sinon.stub();
1582
+ breakouts.keepEditLockAlive();
1583
+ const firstIntervalID = breakouts.intervalID;
1584
+ breakouts.keepEditLockAlive();
1585
+
1586
+ assert.calledWithExactly(window.clearInterval, firstIntervalID);
1587
+ clock.restore();
1588
+ });
1589
+ });
1590
+
1591
+ describe('#assign', () => {
1592
+ it('assign members and emails to a breakout session', async () => {
1593
+ breakouts.assign = sinon.stub().returns(Promise.resolve('ASSIGN_RETURN_VALUE'));
1594
+ const params = [
1595
+ {id: 'sessionId', memberIds: ['111'], emails: ['111@cisco.com'], anyone: true},
1596
+ ];
1597
+ const result = await breakouts.assign(params);
1598
+ assert.calledOnceWithExactly(breakouts.assign, params);
1599
+ assert.equal(result, 'ASSIGN_RETURN_VALUE');
1600
+ });
1601
+ it('assign only members to a breakout session', async () => {
1602
+ breakouts.assign = sinon.stub().returns(Promise.resolve('ASSIGN_RETURN_VALUE'));
1603
+ const params = [{id: 'sessionId', memberIds: ['111']}];
1604
+ const result = await breakouts.assign(params);
1605
+ assert.calledOnceWithExactly(breakouts.assign, params);
1606
+ assert.equal(result, 'ASSIGN_RETURN_VALUE');
1607
+ });
1608
+ it('assign only emails to a breakout session', async () => {
1609
+ breakouts.assign = sinon.stub().returns(Promise.resolve('ASSIGN_RETURN_VALUE'));
1610
+ const params = [{id: 'sessionId', emails: ['111@cisco.com']}];
1611
+ const result = await breakouts.assign(params);
1612
+ assert.calledOnceWithExactly(breakouts.assign, params);
1613
+ assert.equal(result, 'ASSIGN_RETURN_VALUE');
1614
+ });
1615
+
1616
+ it('called with editlock', async () => {
1617
+ breakouts.request = sinon.stub().returns(Promise.resolve('ASSIGN_RETURN_VALUE'));
1618
+ breakouts.editLock = {
1619
+ token: 'token1',
1620
+ };
1621
+ const params = [{id: 'sessionId', emails: ['111@cisco.com'], memberIds: []}];
1622
+ await breakouts.assign(params);
1623
+ const args = breakouts.request.getCall(0).args[0];
1624
+ expect(args).to.be.an('object', {
1625
+ method: 'PUT',
1626
+ uri: 'url',
541
1627
  body: {
542
- "errorCode":BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
543
- "message":"Edit lock token mismatch"
1628
+ editlock: {token: 'token1', refresh: true},
1629
+ groups: {
1630
+ id: 'sessionId',
1631
+ sessions: [
1632
+ {
1633
+ id: 'sessionId',
1634
+ assigned: [],
1635
+ assignedEmails: ['111@cisco.com'],
1636
+ anyoneCanJoin: false,
1637
+ },
1638
+ ],
1639
+ },
1640
+ },
1641
+ });
1642
+ });
1643
+ });
1644
+
1645
+ describe('#queryPreAssignments', () => {
1646
+ it('makes the expected query', async () => {
1647
+ const mockPreAssignments = [
1648
+ {
1649
+ sessions: [
1650
+ {
1651
+ name: 'Breakout session 1',
1652
+ assignedEmails: ['aa@aa.com', 'bb@bb.com', 'cc@cc.com'],
1653
+ anyoneCanJoin: false,
1654
+ },
1655
+ {
1656
+ name: 'Breakout session 2',
1657
+ anyoneCanJoin: false,
1658
+ },
1659
+ {
1660
+ name: 'Breakout session 3',
1661
+ assignedEmails: ['cc@cc.com'],
1662
+ anyoneCanJoin: false,
1663
+ },
1664
+ ],
1665
+ unassignedInvitees: {
1666
+ emails: ['dd@dd.com'],
1667
+ },
1668
+ type: 'BREAKOUT',
1669
+ },
1670
+ ];
1671
+ webex.request.returns(
1672
+ Promise.resolve({
1673
+ body: {
1674
+ groups: mockPreAssignments,
1675
+ },
1676
+ })
1677
+ );
1678
+ breakouts.set('locusUrl', 'test');
1679
+
1680
+ await breakouts.queryPreAssignments();
1681
+ assert.calledOnceWithExactly(webex.request, {
1682
+ uri: 'url/preassignments',
1683
+ qs: {
1684
+ locusUrl: 'dGVzdA==',
544
1685
  }
545
- }));
1686
+ });
546
1687
 
547
- await assert.isRejected(
548
- breakouts.create(sessions),
549
- BreakoutEditLockedError,
550
- 'Edit lock token mismatch'
1688
+ assert.deepEqual(breakouts.preAssignments, mockPreAssignments);
1689
+ });
1690
+
1691
+ it('rejects when no pre-assignments created for this meeting', async () => {
1692
+ const response = {
1693
+ statusCode: 404,
1694
+ body: {
1695
+ errorCode: 201404004,
1696
+ message: 'No pre-assignments created for this meeting',
1697
+ },
1698
+ };
1699
+ webex.request.rejects(response);
1700
+ LoggerProxy.logger.error = sinon.stub();
1701
+ const result = await breakouts.queryPreAssignments({enableBreakoutSession: true, hasBreakoutPreAssignments: true});
1702
+ await testUtils.flushPromises();
1703
+ assert.calledOnceWithExactly(
1704
+ LoggerProxy.logger.error,
1705
+ 'Meeting:breakouts#queryPreAssignments failed',
1706
+ response
551
1707
  );
552
1708
  });
1709
+
1710
+ it('fail when no correct params', () => {
1711
+
1712
+ assert.deepEqual(breakouts.queryPreAssignments(undefined), undefined);
1713
+
1714
+ assert.deepEqual(breakouts.queryPreAssignments({}), undefined);
1715
+
1716
+ assert.deepEqual(breakouts.queryPreAssignments({ enableBreakoutSession: true, hasBreakoutPreAssignments: false }), undefined);
1717
+
1718
+ assert.deepEqual(breakouts.queryPreAssignments({ enableBreakoutSession: false, hasBreakoutPreAssignments: true }), undefined);
1719
+
1720
+ assert.deepEqual(breakouts.queryPreAssignments({ enableBreakoutSession: false, hasBreakoutPreAssignments: false }), undefined);
1721
+
1722
+ });
1723
+
1724
+ });
1725
+
1726
+ describe('#dynamicAssign', () => {
1727
+ it('should make a PUT request with correct body and return the result', async () => {
1728
+ breakouts.dynamicAssign = sinon.stub().returns(Promise.resolve('REQUEST_RETURN_VALUE'));
1729
+
1730
+ const expectedBody = {
1731
+ groups: [
1732
+ {
1733
+ id: 'breakoutGroup1',
1734
+ sessions: [
1735
+ {
1736
+ id: 'session1',
1737
+ participants: ['participant1', 'participant2'],
1738
+ targetState: 'JOINED',
1739
+ },
1740
+ ],
1741
+ },
1742
+ ],
1743
+ editlock: {
1744
+ token: 'abcdefg',
1745
+ },
1746
+ };
1747
+
1748
+ const result = await breakouts.dynamicAssign(expectedBody);
1749
+
1750
+ assert.calledOnceWithExactly(breakouts.dynamicAssign, expectedBody);
1751
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
1752
+ });
1753
+ });
1754
+
1755
+ describe('#triggerReturnToMainEvent', () => {
1756
+ const checkTrigger = ({breakout, shouldTrigger}) => {
1757
+ breakouts.trigger = sinon.stub();
1758
+ breakouts.triggerReturnToMainEvent(breakout);
1759
+ if (shouldTrigger) {
1760
+ assert.calledOnceWithExactly(breakouts.trigger, BREAKOUTS.EVENTS.ASK_RETURN_TO_MAIN);
1761
+ } else {
1762
+ assert.notCalled(breakouts.trigger);
1763
+ }
1764
+ }
1765
+ it('should trigger ASK_RETURN_TO_MAIN event correctly', () => {
1766
+ const breakout = {
1767
+ isMain: true,
1768
+ requested: true
1769
+ };
1770
+ checkTrigger({breakout, shouldTrigger: true})
1771
+ });
1772
+
1773
+ it('should not trigger ASK_RETURN_TO_MAIN event when sessionType is not MAIN', () => {
1774
+ const breakout = {
1775
+ isMain: false,
1776
+ requested: true
1777
+ };
1778
+ checkTrigger({breakout, shouldTrigger: false});
1779
+ });
1780
+
1781
+ it('should not trigger ASK_RETURN_TO_MAIN event when session is not requested', () => {
1782
+ const breakout = {
1783
+ isMain: true,
1784
+ requested: false
1785
+ };
1786
+ checkTrigger({breakout, shouldTrigger: false})
1787
+ });
553
1788
  });
554
1789
  });
555
1790
  });