@webex/plugin-meetings 3.0.0-beta.31 → 3.0.0-beta.310

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 (378) 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 -11
  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 +61 -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/meeting/in-meeting-actions.js +82 -2
  67. package/dist/meeting/in-meeting-actions.js.map +1 -1
  68. package/dist/meeting/index.js +3777 -2929
  69. package/dist/meeting/index.js.map +1 -1
  70. package/dist/meeting/locusMediaRequest.js +292 -0
  71. package/dist/meeting/locusMediaRequest.js.map +1 -0
  72. package/dist/meeting/muteState.js +230 -124
  73. package/dist/meeting/muteState.js.map +1 -1
  74. package/dist/meeting/request.js +260 -196
  75. package/dist/meeting/request.js.map +1 -1
  76. package/dist/meeting/util.js +601 -417
  77. package/dist/meeting/util.js.map +1 -1
  78. package/dist/meeting-info/index.js +73 -7
  79. package/dist/meeting-info/index.js.map +1 -1
  80. package/dist/meeting-info/meeting-info-v2.js +192 -51
  81. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  82. package/dist/meeting-info/util.js +1 -1
  83. package/dist/meeting-info/util.js.map +1 -1
  84. package/dist/meeting-info/utilv2.js +36 -36
  85. package/dist/meeting-info/utilv2.js.map +1 -1
  86. package/dist/meetings/collection.js +39 -0
  87. package/dist/meetings/collection.js.map +1 -1
  88. package/dist/meetings/index.js +415 -115
  89. package/dist/meetings/index.js.map +1 -1
  90. package/dist/meetings/meetings.types.js +7 -0
  91. package/dist/meetings/meetings.types.js.map +1 -0
  92. package/dist/meetings/request.js +2 -0
  93. package/dist/meetings/request.js.map +1 -1
  94. package/dist/meetings/util.js +72 -6
  95. package/dist/meetings/util.js.map +1 -1
  96. package/dist/member/index.js +58 -0
  97. package/dist/member/index.js.map +1 -1
  98. package/dist/member/types.js +25 -0
  99. package/dist/member/types.js.map +1 -0
  100. package/dist/member/util.js +132 -25
  101. package/dist/member/util.js.map +1 -1
  102. package/dist/members/collection.js +10 -0
  103. package/dist/members/collection.js.map +1 -1
  104. package/dist/members/index.js +102 -6
  105. package/dist/members/index.js.map +1 -1
  106. package/dist/members/request.js +106 -38
  107. package/dist/members/request.js.map +1 -1
  108. package/dist/members/types.js +15 -0
  109. package/dist/members/types.js.map +1 -0
  110. package/dist/members/util.js +326 -232
  111. package/dist/members/util.js.map +1 -1
  112. package/dist/metrics/constants.js +13 -5
  113. package/dist/metrics/constants.js.map +1 -1
  114. package/dist/metrics/index.js +1 -468
  115. package/dist/metrics/index.js.map +1 -1
  116. package/dist/multistream/mediaRequestManager.js +238 -49
  117. package/dist/multistream/mediaRequestManager.js.map +1 -1
  118. package/dist/multistream/receiveSlot.js +29 -16
  119. package/dist/multistream/receiveSlot.js.map +1 -1
  120. package/dist/multistream/receiveSlotManager.js +39 -36
  121. package/dist/multistream/receiveSlotManager.js.map +1 -1
  122. package/dist/multistream/remoteMedia.js +44 -18
  123. package/dist/multistream/remoteMedia.js.map +1 -1
  124. package/dist/multistream/remoteMediaGroup.js +60 -3
  125. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  126. package/dist/multistream/remoteMediaManager.js +209 -59
  127. package/dist/multistream/remoteMediaManager.js.map +1 -1
  128. package/dist/multistream/sendSlotManager.js +233 -0
  129. package/dist/multistream/sendSlotManager.js.map +1 -0
  130. package/dist/reachability/index.js +225 -59
  131. package/dist/reachability/index.js.map +1 -1
  132. package/dist/reachability/request.js +17 -8
  133. package/dist/reachability/request.js.map +1 -1
  134. package/dist/reconnection-manager/index.js +201 -156
  135. package/dist/reconnection-manager/index.js.map +1 -1
  136. package/dist/recording-controller/index.js +21 -2
  137. package/dist/recording-controller/index.js.map +1 -1
  138. package/dist/recording-controller/util.js +9 -8
  139. package/dist/recording-controller/util.js.map +1 -1
  140. package/dist/roap/index.js +62 -32
  141. package/dist/roap/index.js.map +1 -1
  142. package/dist/roap/request.js +112 -97
  143. package/dist/roap/request.js.map +1 -1
  144. package/dist/roap/turnDiscovery.js +95 -36
  145. package/dist/roap/turnDiscovery.js.map +1 -1
  146. package/dist/rtcMetrics/constants.js +12 -0
  147. package/dist/rtcMetrics/constants.js.map +1 -0
  148. package/dist/rtcMetrics/index.js +117 -0
  149. package/dist/rtcMetrics/index.js.map +1 -0
  150. package/dist/statsAnalyzer/index.js +86 -78
  151. package/dist/statsAnalyzer/index.js.map +1 -1
  152. package/dist/statsAnalyzer/mqaUtil.js +11 -10
  153. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  154. package/dist/types/annotation/annotation.types.d.ts +42 -0
  155. package/dist/types/annotation/constants.d.ts +31 -0
  156. package/dist/types/annotation/index.d.ts +117 -0
  157. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  158. package/dist/types/breakouts/events.d.ts +8 -0
  159. package/dist/types/breakouts/utils.d.ts +14 -0
  160. package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
  161. package/dist/types/common/errors/reclaim-host-role-errors.d.ts +60 -0
  162. package/dist/types/common/errors/webex-errors.d.ts +25 -1
  163. package/dist/types/common/logs/request.d.ts +2 -0
  164. package/dist/types/common/queue.d.ts +9 -7
  165. package/dist/types/config.d.ts +1 -7
  166. package/dist/types/constants.d.ts +194 -24
  167. package/dist/types/controls-options-manager/enums.d.ts +11 -1
  168. package/dist/types/controls-options-manager/index.d.ts +17 -1
  169. package/dist/types/controls-options-manager/types.d.ts +43 -0
  170. package/dist/types/controls-options-manager/util.d.ts +1 -7
  171. package/dist/types/index.d.ts +6 -4
  172. package/dist/types/interpretation/collection.d.ts +5 -0
  173. package/dist/types/interpretation/index.d.ts +5 -0
  174. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  175. package/dist/types/locus-info/index.d.ts +57 -4
  176. package/dist/types/locus-info/parser.d.ts +67 -6
  177. package/dist/types/media/index.d.ts +2 -0
  178. package/dist/types/media/properties.d.ts +34 -48
  179. package/dist/types/meeting/in-meeting-actions.d.ts +82 -2
  180. package/dist/types/meeting/index.d.ts +463 -510
  181. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  182. package/dist/types/meeting/muteState.d.ts +99 -23
  183. package/dist/types/meeting/request.d.ts +72 -43
  184. package/dist/types/meeting/util.d.ts +101 -1
  185. package/dist/types/meeting-info/index.d.ts +13 -1
  186. package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
  187. package/dist/types/meetings/collection.d.ts +17 -0
  188. package/dist/types/meetings/index.d.ts +98 -20
  189. package/dist/types/meetings/meetings.types.d.ts +4 -0
  190. package/dist/types/member/index.d.ts +14 -0
  191. package/dist/types/member/types.d.ts +32 -0
  192. package/dist/types/members/collection.d.ts +5 -0
  193. package/dist/types/members/index.d.ts +35 -2
  194. package/dist/types/members/request.d.ts +73 -9
  195. package/dist/types/members/types.d.ts +25 -0
  196. package/dist/types/members/util.d.ts +214 -1
  197. package/dist/types/metrics/constants.d.ts +12 -4
  198. package/dist/types/metrics/index.d.ts +4 -119
  199. package/dist/types/multistream/mediaRequestManager.d.ts +73 -5
  200. package/dist/types/multistream/receiveSlot.d.ts +13 -11
  201. package/dist/types/multistream/receiveSlotManager.d.ts +14 -4
  202. package/dist/types/multistream/remoteMedia.d.ts +8 -29
  203. package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
  204. package/dist/types/multistream/remoteMediaManager.d.ts +46 -2
  205. package/dist/types/multistream/sendSlotManager.d.ts +61 -0
  206. package/dist/types/reachability/index.d.ts +61 -7
  207. package/dist/types/reachability/request.d.ts +7 -3
  208. package/dist/types/reconnection-manager/index.d.ts +9 -0
  209. package/dist/types/recording-controller/index.d.ts +15 -1
  210. package/dist/types/recording-controller/util.d.ts +5 -4
  211. package/dist/types/roap/index.d.ts +2 -1
  212. package/dist/types/roap/request.d.ts +15 -11
  213. package/dist/types/roap/turnDiscovery.d.ts +21 -3
  214. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  215. package/dist/types/rtcMetrics/index.d.ts +47 -0
  216. package/dist/types/statsAnalyzer/index.d.ts +7 -1
  217. package/dist/types/webinar/collection.d.ts +16 -0
  218. package/dist/types/webinar/index.d.ts +5 -0
  219. package/dist/webinar/collection.js +44 -0
  220. package/dist/webinar/collection.js.map +1 -0
  221. package/dist/webinar/index.js +69 -0
  222. package/dist/webinar/index.js.map +1 -0
  223. package/package.json +23 -20
  224. package/src/annotation/annotation.types.ts +50 -0
  225. package/src/annotation/constants.ts +36 -0
  226. package/src/annotation/index.ts +328 -0
  227. package/src/breakouts/README.md +42 -12
  228. package/src/breakouts/breakout.ts +67 -9
  229. package/src/breakouts/edit-lock-error.ts +25 -0
  230. package/src/breakouts/events.ts +56 -0
  231. package/src/breakouts/index.ts +592 -20
  232. package/src/breakouts/utils.ts +42 -0
  233. package/src/common/errors/no-meeting-info.ts +24 -0
  234. package/src/common/errors/reclaim-host-role-errors.ts +134 -0
  235. package/src/common/errors/webex-errors.ts +44 -2
  236. package/src/common/logs/logger-proxy.ts +1 -1
  237. package/src/common/logs/request.ts +5 -1
  238. package/src/common/queue.ts +22 -8
  239. package/src/config.ts +4 -10
  240. package/src/constants.ts +221 -19
  241. package/src/controls-options-manager/enums.ts +12 -0
  242. package/src/controls-options-manager/index.ts +116 -21
  243. package/src/controls-options-manager/types.ts +59 -0
  244. package/src/controls-options-manager/util.ts +294 -14
  245. package/src/index.ts +40 -0
  246. package/src/interpretation/README.md +60 -0
  247. package/src/interpretation/collection.ts +19 -0
  248. package/src/interpretation/index.ts +332 -0
  249. package/src/interpretation/siLanguage.ts +18 -0
  250. package/src/locus-info/controlsUtils.ts +108 -0
  251. package/src/locus-info/index.ts +413 -59
  252. package/src/locus-info/infoUtils.ts +10 -2
  253. package/src/locus-info/mediaSharesUtils.ts +64 -0
  254. package/src/locus-info/parser.ts +258 -47
  255. package/src/locus-info/selfUtils.ts +81 -5
  256. package/src/media/index.ts +102 -122
  257. package/src/media/properties.ts +87 -110
  258. package/src/meeting/in-meeting-actions.ts +163 -3
  259. package/src/meeting/index.ts +3132 -2541
  260. package/src/meeting/locusMediaRequest.ts +313 -0
  261. package/src/meeting/muteState.ts +229 -131
  262. package/src/meeting/request.ts +177 -121
  263. package/src/meeting/util.ts +588 -394
  264. package/src/meeting-info/index.ts +81 -8
  265. package/src/meeting-info/meeting-info-v2.ts +170 -14
  266. package/src/meeting-info/util.ts +1 -1
  267. package/src/meeting-info/utilv2.ts +23 -23
  268. package/src/meetings/collection.ts +33 -0
  269. package/src/meetings/index.ts +445 -123
  270. package/src/meetings/meetings.types.ts +12 -0
  271. package/src/meetings/request.ts +2 -0
  272. package/src/meetings/util.ts +80 -11
  273. package/src/member/index.ts +58 -0
  274. package/src/member/types.ts +38 -0
  275. package/src/member/util.ts +141 -25
  276. package/src/members/collection.ts +8 -0
  277. package/src/members/index.ts +134 -8
  278. package/src/members/request.ts +97 -17
  279. package/src/members/types.ts +29 -0
  280. package/src/members/util.ts +333 -240
  281. package/src/metrics/constants.ts +12 -4
  282. package/src/metrics/index.ts +1 -490
  283. package/src/multistream/mediaRequestManager.ts +289 -79
  284. package/src/multistream/receiveSlot.ts +31 -17
  285. package/src/multistream/receiveSlotManager.ts +34 -24
  286. package/src/multistream/remoteMedia.ts +27 -2
  287. package/src/multistream/remoteMediaGroup.ts +59 -0
  288. package/src/multistream/remoteMediaManager.ts +148 -30
  289. package/src/multistream/sendSlotManager.ts +170 -0
  290. package/src/reachability/index.ts +228 -37
  291. package/src/reachability/request.ts +17 -8
  292. package/src/reconnection-manager/index.ts +83 -56
  293. package/src/recording-controller/index.ts +20 -3
  294. package/src/recording-controller/util.ts +26 -9
  295. package/src/roap/index.ts +63 -32
  296. package/src/roap/request.ts +100 -104
  297. package/src/roap/turnDiscovery.ts +48 -26
  298. package/src/rtcMetrics/constants.ts +3 -0
  299. package/src/rtcMetrics/index.ts +100 -0
  300. package/src/statsAnalyzer/index.ts +105 -91
  301. package/src/statsAnalyzer/mqaUtil.ts +13 -14
  302. package/src/webinar/collection.ts +31 -0
  303. package/src/webinar/index.ts +62 -0
  304. package/test/integration/spec/converged-space-meetings.js +60 -3
  305. package/test/integration/spec/journey.js +320 -261
  306. package/test/integration/spec/space-meeting.js +76 -3
  307. package/test/unit/spec/annotation/index.ts +418 -0
  308. package/test/unit/spec/breakouts/breakout.ts +118 -28
  309. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  310. package/test/unit/spec/breakouts/events.ts +89 -0
  311. package/test/unit/spec/breakouts/index.ts +1395 -69
  312. package/test/unit/spec/breakouts/utils.js +52 -1
  313. package/test/unit/spec/common/queue.js +31 -2
  314. package/test/unit/spec/controls-options-manager/index.js +163 -0
  315. package/test/unit/spec/controls-options-manager/util.js +576 -60
  316. package/test/unit/spec/fixture/locus.js +1 -0
  317. package/test/unit/spec/interpretation/collection.ts +15 -0
  318. package/test/unit/spec/interpretation/index.ts +589 -0
  319. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  320. package/test/unit/spec/locus-info/controlsUtils.js +316 -43
  321. package/test/unit/spec/locus-info/index.js +1304 -33
  322. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  323. package/test/unit/spec/locus-info/lib/SeqCmp.json +16 -0
  324. package/test/unit/spec/locus-info/mediaSharesUtils.ts +32 -0
  325. package/test/unit/spec/locus-info/parser.js +116 -35
  326. package/test/unit/spec/locus-info/selfConstant.js +27 -4
  327. package/test/unit/spec/locus-info/selfUtils.js +208 -17
  328. package/test/unit/spec/media/index.ts +104 -37
  329. package/test/unit/spec/media/properties.ts +2 -2
  330. package/test/unit/spec/meeting/in-meeting-actions.ts +81 -3
  331. package/test/unit/spec/meeting/index.js +5216 -1956
  332. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  333. package/test/unit/spec/meeting/muteState.js +408 -208
  334. package/test/unit/spec/meeting/request.js +483 -49
  335. package/test/unit/spec/meeting/utils.js +679 -64
  336. package/test/unit/spec/meeting-info/index.js +300 -0
  337. package/test/unit/spec/meeting-info/meetinginfov2.js +526 -5
  338. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  339. package/test/unit/spec/meetings/collection.js +26 -0
  340. package/test/unit/spec/meetings/index.js +1011 -205
  341. package/test/unit/spec/meetings/utils.js +202 -2
  342. package/test/unit/spec/member/index.js +61 -6
  343. package/test/unit/spec/member/util.js +510 -34
  344. package/test/unit/spec/members/index.js +432 -1
  345. package/test/unit/spec/members/request.js +206 -27
  346. package/test/unit/spec/members/utils.js +210 -0
  347. package/test/unit/spec/metrics/index.js +1 -50
  348. package/test/unit/spec/multistream/mediaRequestManager.ts +803 -162
  349. package/test/unit/spec/multistream/receiveSlot.ts +28 -20
  350. package/test/unit/spec/multistream/receiveSlotManager.ts +32 -30
  351. package/test/unit/spec/multistream/remoteMedia.ts +30 -0
  352. package/test/unit/spec/multistream/remoteMediaGroup.ts +266 -0
  353. package/test/unit/spec/multistream/remoteMediaManager.ts +326 -0
  354. package/test/unit/spec/multistream/sendSlotManager.ts +242 -0
  355. package/test/unit/spec/reachability/index.ts +549 -9
  356. package/test/unit/spec/reachability/request.js +68 -0
  357. package/test/unit/spec/reconnection-manager/index.js +85 -9
  358. package/test/unit/spec/recording-controller/index.js +294 -218
  359. package/test/unit/spec/recording-controller/util.js +223 -96
  360. package/test/unit/spec/roap/index.ts +178 -64
  361. package/test/unit/spec/roap/request.ts +203 -85
  362. package/test/unit/spec/roap/turnDiscovery.ts +82 -36
  363. package/test/unit/spec/rtcMetrics/index.ts +73 -0
  364. package/test/unit/spec/stats-analyzer/index.js +136 -2
  365. package/test/unit/spec/webinar/collection.ts +13 -0
  366. package/test/unit/spec/webinar/index.ts +60 -0
  367. package/test/utils/integrationTestUtils.js +46 -0
  368. package/test/utils/testUtils.js +0 -52
  369. package/dist/meeting/effectsState.js +0 -262
  370. package/dist/meeting/effectsState.js.map +0 -1
  371. package/dist/metrics/config.js +0 -299
  372. package/dist/metrics/config.js.map +0 -1
  373. package/dist/types/meeting/effectsState.d.ts +0 -42
  374. package/dist/types/metrics/config.d.ts +0 -178
  375. package/src/index.js +0 -16
  376. package/src/meeting/effectsState.ts +0 -211
  377. package/src/metrics/config.ts +0 -495
  378. package/test/unit/spec/meeting/effectsState.js +0 -285
@@ -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
-
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
121
+ });
122
+
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
+ });
128
+
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
+ });
134
+
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
40
139
  });
41
140
 
42
- breakouts.set('status', 'something');
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
+ });
43
149
 
44
- assert.isFalse(called);
150
+ const breakoutClosingHandler = sinon.stub();
151
+ breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.BREAKOUTS_CLOSING, breakoutClosingHandler);
45
152
 
46
- breakouts.set({'status': BREAKOUTS.STATUS.CLOSING});
153
+ breakouts.set({
154
+ sessionType: BREAKOUTS.SESSION_TYPES.MAIN,
155
+ groups: [{status: BREAKOUTS.STATUS.CLOSING}],
156
+ status: undefined
157
+ });
47
158
 
48
- assert.isTrue(called);
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,33 +904,887 @@ 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
1239
+ editlock: {
1240
+ token: 'editToken',
1241
+ },
1242
+ enableBreakoutSession: true,
1243
+ },
1244
+ });
1245
+
1246
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
1247
+ });
1248
+ });
1249
+
1250
+ describe('delete', () => {
1251
+ it('makes the request as expected', async () => {
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
+ );
1265
+
1266
+ breakouts._setManageGroups = sinon.stub();
1267
+ const result = await breakouts.clearSessions();
1268
+ assert.calledOnceWithExactly(webex.request, {
1269
+ method: 'PUT',
1270
+ uri: 'url',
1271
+ body: {
1272
+ groups: [
1273
+ {
1274
+ action: BREAKOUTS.ACTION.DELETE,
1275
+ },
1276
+ ],
1277
+ },
1278
+ });
1279
+
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
+ });
1291
+ });
1292
+
1293
+ it('rejects when edit lock token mismatch', async () => {
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
+ );
1302
+
1303
+ await assert.isRejected(
1304
+ breakouts.clearSessions(),
1305
+ BreakoutEditLockedError,
1306
+ 'Edit lock token mismatch'
1307
+ );
1308
+ });
1309
+ });
1310
+
1311
+ describe('create', () => {
1312
+ it('response not include groups info', async () => {
1313
+ const sessions = [{name: 'session1', anyoneCanJoin: true}];
1314
+ const result = await breakouts.create({sessions});
1315
+
1316
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
1317
+ });
1318
+
1319
+ it('response include groups info', async () => {
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
+ );
1335
+
1336
+ const result = await breakouts.create({sessions});
1337
+
1338
+ assert.equal(breakouts.manageGroups[0].id, '455556a4-37cd-4baa-89bc-8730581a1cc0');
1339
+ });
1340
+
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();
1366
+
1367
+ breakouts.enableAndLockBreakout();
1368
+
1369
+ assert.calledOnceWithExactly(breakouts.lockBreakout);
1370
+ });
1371
+
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();
1424
+
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',
1627
+ body: {
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==',
457
1685
  }
458
1686
  });
459
1687
 
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
1707
+ );
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);
460
1751
  assert.equal(result, 'REQUEST_RETURN_VALUE');
461
1752
  });
462
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
+ });
1788
+ });
463
1789
  });
464
1790
  });