@webex/plugin-meetings 3.0.0-beta.28 → 3.0.0-beta.280

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 (363) 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 +114 -14
  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 +763 -31
  15. package/dist/breakouts/index.js.map +1 -1
  16. package/dist/breakouts/request.js +78 -0
  17. package/dist/breakouts/request.js.map +1 -0
  18. package/dist/breakouts/utils.js +67 -0
  19. package/dist/breakouts/utils.js.map +1 -0
  20. package/dist/common/errors/no-meeting-info.js +51 -0
  21. package/dist/common/errors/no-meeting-info.js.map +1 -0
  22. package/dist/common/errors/webex-errors.js +28 -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/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 +203 -28
  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 +112 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/interpretation/collection.js +23 -0
  43. package/dist/interpretation/collection.js.map +1 -0
  44. package/dist/interpretation/index.js +366 -0
  45. package/dist/interpretation/index.js.map +1 -0
  46. package/dist/interpretation/siLanguage.js +25 -0
  47. package/dist/interpretation/siLanguage.js.map +1 -0
  48. package/dist/locus-info/controlsUtils.js +91 -2
  49. package/dist/locus-info/controlsUtils.js.map +1 -1
  50. package/dist/locus-info/index.js +383 -62
  51. package/dist/locus-info/index.js.map +1 -1
  52. package/dist/locus-info/infoUtils.js +7 -1
  53. package/dist/locus-info/infoUtils.js.map +1 -1
  54. package/dist/locus-info/mediaSharesUtils.js +57 -1
  55. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  56. package/dist/locus-info/parser.js +249 -72
  57. package/dist/locus-info/parser.js.map +1 -1
  58. package/dist/locus-info/selfUtils.js +89 -14
  59. package/dist/locus-info/selfUtils.js.map +1 -1
  60. package/dist/media/index.js +58 -116
  61. package/dist/media/index.js.map +1 -1
  62. package/dist/media/properties.js +72 -123
  63. package/dist/media/properties.js.map +1 -1
  64. package/dist/meeting/in-meeting-actions.js +82 -2
  65. package/dist/meeting/in-meeting-actions.js.map +1 -1
  66. package/dist/meeting/index.js +3123 -2814
  67. package/dist/meeting/index.js.map +1 -1
  68. package/dist/meeting/locusMediaRequest.js +292 -0
  69. package/dist/meeting/locusMediaRequest.js.map +1 -0
  70. package/dist/meeting/muteState.js +230 -124
  71. package/dist/meeting/muteState.js.map +1 -1
  72. package/dist/meeting/request.js +256 -196
  73. package/dist/meeting/request.js.map +1 -1
  74. package/dist/meeting/util.js +601 -417
  75. package/dist/meeting/util.js.map +1 -1
  76. package/dist/meeting-info/index.js +73 -7
  77. package/dist/meeting-info/index.js.map +1 -1
  78. package/dist/meeting-info/meeting-info-v2.js +192 -51
  79. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  80. package/dist/meeting-info/util.js +1 -1
  81. package/dist/meeting-info/util.js.map +1 -1
  82. package/dist/meeting-info/utilv2.js +36 -36
  83. package/dist/meeting-info/utilv2.js.map +1 -1
  84. package/dist/meetings/collection.js +22 -0
  85. package/dist/meetings/collection.js.map +1 -1
  86. package/dist/meetings/index.js +394 -94
  87. package/dist/meetings/index.js.map +1 -1
  88. package/dist/meetings/meetings.types.js +7 -0
  89. package/dist/meetings/meetings.types.js.map +1 -0
  90. package/dist/meetings/request.js +2 -0
  91. package/dist/meetings/request.js.map +1 -1
  92. package/dist/meetings/util.js +71 -1
  93. package/dist/meetings/util.js.map +1 -1
  94. package/dist/member/index.js +49 -0
  95. package/dist/member/index.js.map +1 -1
  96. package/dist/member/types.js +25 -0
  97. package/dist/member/types.js.map +1 -0
  98. package/dist/member/util.js +121 -25
  99. package/dist/member/util.js.map +1 -1
  100. package/dist/members/collection.js +10 -0
  101. package/dist/members/collection.js.map +1 -1
  102. package/dist/members/index.js +86 -6
  103. package/dist/members/index.js.map +1 -1
  104. package/dist/members/request.js +106 -38
  105. package/dist/members/request.js.map +1 -1
  106. package/dist/members/types.js +15 -0
  107. package/dist/members/types.js.map +1 -0
  108. package/dist/members/util.js +316 -233
  109. package/dist/members/util.js.map +1 -1
  110. package/dist/metrics/constants.js +12 -5
  111. package/dist/metrics/constants.js.map +1 -1
  112. package/dist/metrics/index.js +1 -468
  113. package/dist/metrics/index.js.map +1 -1
  114. package/dist/multistream/mediaRequestManager.js +238 -49
  115. package/dist/multistream/mediaRequestManager.js.map +1 -1
  116. package/dist/multistream/receiveSlot.js +40 -16
  117. package/dist/multistream/receiveSlot.js.map +1 -1
  118. package/dist/multistream/receiveSlotManager.js +39 -36
  119. package/dist/multistream/receiveSlotManager.js.map +1 -1
  120. package/dist/multistream/remoteMedia.js +44 -18
  121. package/dist/multistream/remoteMedia.js.map +1 -1
  122. package/dist/multistream/remoteMediaGroup.js +60 -3
  123. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  124. package/dist/multistream/remoteMediaManager.js +209 -59
  125. package/dist/multistream/remoteMediaManager.js.map +1 -1
  126. package/dist/multistream/sendSlotManager.js +233 -0
  127. package/dist/multistream/sendSlotManager.js.map +1 -0
  128. package/dist/reachability/index.js +225 -59
  129. package/dist/reachability/index.js.map +1 -1
  130. package/dist/reachability/request.js +17 -8
  131. package/dist/reachability/request.js.map +1 -1
  132. package/dist/reconnection-manager/index.js +199 -154
  133. package/dist/reconnection-manager/index.js.map +1 -1
  134. package/dist/recording-controller/index.js +21 -2
  135. package/dist/recording-controller/index.js.map +1 -1
  136. package/dist/recording-controller/util.js +9 -8
  137. package/dist/recording-controller/util.js.map +1 -1
  138. package/dist/roap/index.js +23 -29
  139. package/dist/roap/index.js.map +1 -1
  140. package/dist/roap/request.js +112 -97
  141. package/dist/roap/request.js.map +1 -1
  142. package/dist/roap/turnDiscovery.js +96 -36
  143. package/dist/roap/turnDiscovery.js.map +1 -1
  144. package/dist/rtcMetrics/constants.js +12 -0
  145. package/dist/rtcMetrics/constants.js.map +1 -0
  146. package/dist/rtcMetrics/index.js +117 -0
  147. package/dist/rtcMetrics/index.js.map +1 -0
  148. package/dist/statsAnalyzer/index.js +67 -73
  149. package/dist/statsAnalyzer/index.js.map +1 -1
  150. package/dist/statsAnalyzer/mqaUtil.js +11 -10
  151. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  152. package/dist/types/annotation/annotation.types.d.ts +42 -0
  153. package/dist/types/annotation/constants.d.ts +31 -0
  154. package/dist/types/annotation/index.d.ts +117 -0
  155. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  156. package/dist/types/breakouts/events.d.ts +8 -0
  157. package/dist/types/breakouts/request.d.ts +22 -0
  158. package/dist/types/breakouts/utils.d.ts +15 -0
  159. package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
  160. package/dist/types/common/errors/webex-errors.d.ts +13 -1
  161. package/dist/types/common/queue.d.ts +9 -7
  162. package/dist/types/config.d.ts +1 -6
  163. package/dist/types/constants.d.ts +161 -21
  164. package/dist/types/controls-options-manager/enums.d.ts +11 -1
  165. package/dist/types/controls-options-manager/index.d.ts +17 -1
  166. package/dist/types/controls-options-manager/types.d.ts +43 -0
  167. package/dist/types/controls-options-manager/util.d.ts +1 -7
  168. package/dist/types/index.d.ts +6 -4
  169. package/dist/types/interpretation/collection.d.ts +5 -0
  170. package/dist/types/interpretation/index.d.ts +5 -0
  171. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  172. package/dist/types/locus-info/index.d.ts +57 -4
  173. package/dist/types/locus-info/parser.d.ts +67 -6
  174. package/dist/types/media/index.d.ts +2 -0
  175. package/dist/types/media/properties.d.ts +34 -48
  176. package/dist/types/meeting/in-meeting-actions.d.ts +82 -2
  177. package/dist/types/meeting/index.d.ts +345 -507
  178. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  179. package/dist/types/meeting/muteState.d.ts +99 -23
  180. package/dist/types/meeting/request.d.ts +72 -43
  181. package/dist/types/meeting/util.d.ts +101 -1
  182. package/dist/types/meeting-info/index.d.ts +13 -1
  183. package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
  184. package/dist/types/meetings/collection.d.ts +8 -0
  185. package/dist/types/meetings/index.d.ts +88 -12
  186. package/dist/types/meetings/meetings.types.d.ts +4 -0
  187. package/dist/types/member/index.d.ts +13 -0
  188. package/dist/types/member/types.d.ts +32 -0
  189. package/dist/types/members/collection.d.ts +5 -0
  190. package/dist/types/members/index.d.ts +35 -2
  191. package/dist/types/members/request.d.ts +73 -9
  192. package/dist/types/members/types.d.ts +24 -0
  193. package/dist/types/members/util.d.ts +209 -1
  194. package/dist/types/metrics/constants.d.ts +11 -4
  195. package/dist/types/metrics/index.d.ts +4 -119
  196. package/dist/types/multistream/mediaRequestManager.d.ts +73 -5
  197. package/dist/types/multistream/receiveSlot.d.ts +13 -11
  198. package/dist/types/multistream/receiveSlotManager.d.ts +14 -4
  199. package/dist/types/multistream/remoteMedia.d.ts +8 -29
  200. package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
  201. package/dist/types/multistream/remoteMediaManager.d.ts +46 -2
  202. package/dist/types/multistream/sendSlotManager.d.ts +61 -0
  203. package/dist/types/reachability/index.d.ts +61 -7
  204. package/dist/types/reachability/request.d.ts +7 -3
  205. package/dist/types/reconnection-manager/index.d.ts +9 -0
  206. package/dist/types/recording-controller/index.d.ts +15 -1
  207. package/dist/types/recording-controller/util.d.ts +5 -4
  208. package/dist/types/roap/request.d.ts +15 -11
  209. package/dist/types/roap/turnDiscovery.d.ts +18 -1
  210. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  211. package/dist/types/rtcMetrics/index.d.ts +47 -0
  212. package/dist/types/statsAnalyzer/index.d.ts +6 -1
  213. package/package.json +23 -20
  214. package/src/annotation/annotation.types.ts +50 -0
  215. package/src/annotation/constants.ts +36 -0
  216. package/src/annotation/index.ts +328 -0
  217. package/src/breakouts/README.md +44 -14
  218. package/src/breakouts/breakout.ts +87 -9
  219. package/src/breakouts/edit-lock-error.ts +25 -0
  220. package/src/breakouts/events.ts +56 -0
  221. package/src/breakouts/index.ts +646 -18
  222. package/src/breakouts/request.ts +55 -0
  223. package/src/breakouts/utils.ts +57 -0
  224. package/src/common/errors/no-meeting-info.ts +24 -0
  225. package/src/common/errors/webex-errors.ts +27 -2
  226. package/src/common/logs/logger-proxy.ts +1 -1
  227. package/src/common/queue.ts +22 -8
  228. package/src/config.ts +4 -9
  229. package/src/constants.ts +184 -18
  230. package/src/controls-options-manager/enums.ts +12 -0
  231. package/src/controls-options-manager/index.ts +116 -21
  232. package/src/controls-options-manager/types.ts +59 -0
  233. package/src/controls-options-manager/util.ts +294 -14
  234. package/src/index.ts +40 -0
  235. package/src/interpretation/README.md +60 -0
  236. package/src/interpretation/collection.ts +19 -0
  237. package/src/interpretation/index.ts +332 -0
  238. package/src/interpretation/siLanguage.ts +18 -0
  239. package/src/locus-info/controlsUtils.ts +108 -0
  240. package/src/locus-info/index.ts +413 -59
  241. package/src/locus-info/infoUtils.ts +10 -2
  242. package/src/locus-info/mediaSharesUtils.ts +64 -0
  243. package/src/locus-info/parser.ts +258 -47
  244. package/src/locus-info/selfUtils.ts +81 -5
  245. package/src/media/index.ts +100 -122
  246. package/src/media/properties.ts +85 -108
  247. package/src/meeting/in-meeting-actions.ts +163 -3
  248. package/src/meeting/index.ts +2541 -2309
  249. package/src/meeting/locusMediaRequest.ts +313 -0
  250. package/src/meeting/muteState.ts +229 -131
  251. package/src/meeting/request.ts +172 -121
  252. package/src/meeting/util.ts +588 -394
  253. package/src/meeting-info/index.ts +81 -8
  254. package/src/meeting-info/meeting-info-v2.ts +170 -14
  255. package/src/meeting-info/util.ts +1 -1
  256. package/src/meeting-info/utilv2.ts +23 -23
  257. package/src/meetings/collection.ts +20 -0
  258. package/src/meetings/index.ts +428 -108
  259. package/src/meetings/meetings.types.ts +12 -0
  260. package/src/meetings/request.ts +2 -0
  261. package/src/meetings/util.ts +79 -4
  262. package/src/member/index.ts +49 -0
  263. package/src/member/types.ts +38 -0
  264. package/src/member/util.ts +127 -25
  265. package/src/members/collection.ts +8 -0
  266. package/src/members/index.ts +106 -7
  267. package/src/members/request.ts +97 -17
  268. package/src/members/types.ts +28 -0
  269. package/src/members/util.ts +319 -240
  270. package/src/metrics/constants.ts +11 -4
  271. package/src/metrics/index.ts +1 -490
  272. package/src/multistream/mediaRequestManager.ts +289 -79
  273. package/src/multistream/receiveSlot.ts +47 -17
  274. package/src/multistream/receiveSlotManager.ts +34 -24
  275. package/src/multistream/remoteMedia.ts +27 -2
  276. package/src/multistream/remoteMediaGroup.ts +59 -0
  277. package/src/multistream/remoteMediaManager.ts +148 -30
  278. package/src/multistream/sendSlotManager.ts +170 -0
  279. package/src/reachability/index.ts +228 -37
  280. package/src/reachability/request.ts +17 -8
  281. package/src/reconnection-manager/index.ts +81 -54
  282. package/src/recording-controller/index.ts +20 -3
  283. package/src/recording-controller/util.ts +26 -9
  284. package/src/roap/index.ts +23 -30
  285. package/src/roap/request.ts +100 -104
  286. package/src/roap/turnDiscovery.ts +51 -25
  287. package/src/rtcMetrics/constants.ts +3 -0
  288. package/src/rtcMetrics/index.ts +100 -0
  289. package/src/statsAnalyzer/index.ts +88 -88
  290. package/src/statsAnalyzer/mqaUtil.ts +13 -14
  291. package/test/integration/spec/converged-space-meetings.js +60 -3
  292. package/test/integration/spec/journey.js +320 -261
  293. package/test/integration/spec/space-meeting.js +76 -3
  294. package/test/unit/spec/annotation/index.ts +418 -0
  295. package/test/unit/spec/breakouts/breakout.ts +142 -24
  296. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  297. package/test/unit/spec/breakouts/events.ts +89 -0
  298. package/test/unit/spec/breakouts/index.ts +1488 -67
  299. package/test/unit/spec/breakouts/request.ts +104 -0
  300. package/test/unit/spec/breakouts/utils.js +72 -0
  301. package/test/unit/spec/common/queue.js +31 -2
  302. package/test/unit/spec/controls-options-manager/index.js +163 -0
  303. package/test/unit/spec/controls-options-manager/util.js +576 -60
  304. package/test/unit/spec/fixture/locus.js +1 -0
  305. package/test/unit/spec/interpretation/collection.ts +15 -0
  306. package/test/unit/spec/interpretation/index.ts +589 -0
  307. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  308. package/test/unit/spec/locus-info/controlsUtils.js +316 -43
  309. package/test/unit/spec/locus-info/index.js +1304 -33
  310. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  311. package/test/unit/spec/locus-info/lib/SeqCmp.json +16 -0
  312. package/test/unit/spec/locus-info/mediaSharesUtils.ts +32 -0
  313. package/test/unit/spec/locus-info/parser.js +116 -35
  314. package/test/unit/spec/locus-info/selfConstant.js +27 -4
  315. package/test/unit/spec/locus-info/selfUtils.js +208 -17
  316. package/test/unit/spec/media/index.ts +104 -37
  317. package/test/unit/spec/meeting/in-meeting-actions.ts +81 -3
  318. package/test/unit/spec/meeting/index.js +4515 -1932
  319. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  320. package/test/unit/spec/meeting/muteState.js +408 -208
  321. package/test/unit/spec/meeting/request.js +440 -45
  322. package/test/unit/spec/meeting/utils.js +679 -64
  323. package/test/unit/spec/meeting-info/index.js +295 -0
  324. package/test/unit/spec/meeting-info/meetinginfov2.js +521 -5
  325. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  326. package/test/unit/spec/meetings/collection.js +14 -0
  327. package/test/unit/spec/meetings/index.js +1007 -177
  328. package/test/unit/spec/meetings/utils.js +206 -2
  329. package/test/unit/spec/member/index.js +58 -4
  330. package/test/unit/spec/member/util.js +479 -35
  331. package/test/unit/spec/members/index.js +319 -1
  332. package/test/unit/spec/members/request.js +206 -27
  333. package/test/unit/spec/members/utils.js +184 -0
  334. package/test/unit/spec/metrics/index.js +1 -50
  335. package/test/unit/spec/multistream/mediaRequestManager.ts +803 -162
  336. package/test/unit/spec/multistream/receiveSlot.ts +43 -20
  337. package/test/unit/spec/multistream/receiveSlotManager.ts +32 -30
  338. package/test/unit/spec/multistream/remoteMedia.ts +30 -0
  339. package/test/unit/spec/multistream/remoteMediaGroup.ts +266 -0
  340. package/test/unit/spec/multistream/remoteMediaManager.ts +326 -0
  341. package/test/unit/spec/multistream/sendSlotManager.ts +242 -0
  342. package/test/unit/spec/reachability/index.ts +549 -9
  343. package/test/unit/spec/reachability/request.js +68 -0
  344. package/test/unit/spec/reconnection-manager/index.js +84 -9
  345. package/test/unit/spec/recording-controller/index.js +294 -218
  346. package/test/unit/spec/recording-controller/util.js +223 -96
  347. package/test/unit/spec/roap/index.ts +31 -51
  348. package/test/unit/spec/roap/request.ts +203 -85
  349. package/test/unit/spec/roap/turnDiscovery.ts +48 -13
  350. package/test/unit/spec/rtcMetrics/index.ts +68 -0
  351. package/test/unit/spec/stats-analyzer/index.js +64 -2
  352. package/test/utils/integrationTestUtils.js +46 -0
  353. package/test/utils/testUtils.js +0 -52
  354. package/dist/meeting/effectsState.js +0 -262
  355. package/dist/meeting/effectsState.js.map +0 -1
  356. package/dist/metrics/config.js +0 -299
  357. package/dist/metrics/config.js.map +0 -1
  358. package/dist/types/meeting/effectsState.d.ts +0 -42
  359. package/dist/types/metrics/config.d.ts +0 -178
  360. package/src/index.js +0 -16
  361. package/src/meeting/effectsState.ts +0 -211
  362. package/src/metrics/config.ts +0 -495
  363. package/test/unit/spec/meeting/effectsState.js +0 -285
@@ -1,13 +1,82 @@
1
- import {assert} from '@webex/test-helper-chai';
2
- import Breakout from '@webex/plugin-meetings/src/breakouts/breakout';
1
+ import {assert, expect} from '@webex/test-helper-chai';
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';
8
+ import BreakoutEditLockedError from '@webex/plugin-meetings/src/breakouts/edit-lock-error';
9
+ import breakoutEvent from '../../../../src/breakouts/events';
10
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', () => {
@@ -20,10 +89,14 @@ describe('plugin-meetings', () => {
20
89
  webex.internal.llm.on = sinon.stub();
21
90
  webex.internal.mercury.on = sinon.stub();
22
91
  breakouts = new Breakouts({}, {parent: webex});
92
+ breakouts.groupId = 'groupId';
93
+ breakouts.sessionId = 'sessionId';
94
+ breakouts.url = 'url';
23
95
  breakouts.locusUrl = 'locusUrl';
24
96
  breakouts.breakoutServiceUrl = 'breakoutServiceUrl';
25
- breakouts.url = 'url';
26
97
  webex.request = sinon.stub().returns(Promise.resolve('REQUEST_RETURN_VALUE'));
98
+ webex.meetings = {};
99
+ webex.meetings.getMeetingByType = sinon.stub();
27
100
  });
28
101
 
29
102
  describe('#initialize', () => {
@@ -31,19 +104,59 @@ describe('plugin-meetings', () => {
31
104
  assert.equal(breakouts.namespace, 'Meetings');
32
105
  });
33
106
 
34
- it('emits BREAKOUTS_CLOSING event when the status is CLOSING', () => {
35
- let called = false;
36
- breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.BREAKOUTS_CLOSING, () => {
37
- 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
38
139
  });
39
140
 
40
- breakouts.set('status', 'something');
141
+ });
41
142
 
42
- assert.isFalse(called);
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
- breakouts.set({'status': BREAKOUTS.STATUS.CLOSING});
150
+ const breakoutClosingHandler = sinon.stub();
151
+ breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.BREAKOUTS_CLOSING, breakoutClosingHandler);
45
152
 
46
- assert.isTrue(called);
153
+ breakouts.set({
154
+ sessionType: BREAKOUTS.SESSION_TYPES.MAIN,
155
+ groups: [{status: BREAKOUTS.STATUS.CLOSING}],
156
+ status: undefined
157
+ });
158
+
159
+ assert.notCalled(breakoutClosingHandler);
47
160
  });
48
161
 
49
162
  it('debounces querying rosters on add', () => {
@@ -52,20 +165,78 @@ describe('plugin-meetings', () => {
52
165
 
53
166
  assert.calledOnceWithExactly(breakouts.debouncedQueryRosters);
54
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
+ });
55
226
  });
56
227
 
57
228
  describe('#listenToBroadcastMessages', () => {
58
229
  it('triggers message event when a message received', () => {
59
230
  const call = webex.internal.llm.on.getCall(0);
60
231
  const callback = call.args[1];
61
-
232
+
62
233
  assert.equal(call.args[0], 'event:breakout.message');
63
234
 
64
235
  let message;
65
236
 
66
237
  breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.MESSAGE, (event) => {
67
238
  message = event;
68
- })
239
+ });
69
240
 
70
241
  breakouts.currentBreakoutSession.sessionId = 'sessionId';
71
242
 
@@ -74,14 +245,14 @@ describe('plugin-meetings', () => {
74
245
  senderUserId: 'senderUserId',
75
246
  sentTime: 'sentTime',
76
247
  message: 'message',
77
- }
248
+ },
78
249
  });
79
250
 
80
251
  assert.deepEqual(message, {
81
- senderUserId: "senderUserId",
252
+ senderUserId: 'senderUserId',
82
253
  sentTime: 'sentTime',
83
254
  message: 'message',
84
- sessionId: 'sessionId'
255
+ sessionId: 'sessionId',
85
256
  });
86
257
  });
87
258
  });
@@ -90,20 +261,20 @@ describe('plugin-meetings', () => {
90
261
  it('triggers member update event when a roster received', () => {
91
262
  const call = webex.internal.mercury.on.getCall(0);
92
263
  const callback = call.args[1];
93
-
264
+
94
265
  assert.equal(call.args[0], 'event:breakout.roster');
95
266
 
96
267
  let called = false;
97
268
 
98
269
  breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.MEMBERS_UPDATE, () => {
99
270
  called = true;
100
- })
271
+ });
101
272
  breakouts.handleRosterUpdate = sinon.stub();
102
273
 
103
274
  callback({
104
275
  data: {
105
- locus: 'locus'
106
- }
276
+ locus: 'locus',
277
+ },
107
278
  });
108
279
 
109
280
  assert.isTrue(called);
@@ -111,6 +282,33 @@ describe('plugin-meetings', () => {
111
282
  });
112
283
  });
113
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
+
114
312
  describe('#updateBreakout', () => {
115
313
  it('updates the current breakout session', () => {
116
314
  breakouts.updateBreakout({
@@ -124,7 +322,7 @@ describe('plugin-meetings', () => {
124
322
  enableBreakoutSession: true,
125
323
  startTime: 'startTime',
126
324
  status: 'active',
127
- locusUrl: 'locusUrl'
325
+ locusUrl: 'locusUrl',
128
326
  });
129
327
 
130
328
  assert.equal(breakouts.allowBackToMain, true);
@@ -150,10 +348,90 @@ describe('plugin-meetings', () => {
150
348
  assert.equal(breakouts.currentBreakoutSession.assignedCurrent, false);
151
349
  assert.equal(breakouts.currentBreakoutSession.requested, false);
152
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
+ });
153
432
  });
154
433
 
155
434
  describe('#updateBreakoutSessions', () => {
156
-
157
435
  const checkBreakout = (breakout, sessionId, state) => {
158
436
  assert.deepEqual(breakout.attributes, {
159
437
  active: false,
@@ -165,14 +443,13 @@ describe('plugin-meetings', () => {
165
443
  requested: false,
166
444
  url: 'url',
167
445
  sessionId,
168
- ...{[state]: true}
446
+ ...{[state]: true},
169
447
  });
170
- }
448
+ };
171
449
 
172
450
  it('works', () => {
173
-
174
451
  breakouts.set('url', 'url');
175
-
452
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
176
453
  const payload = {
177
454
  breakoutSessions: {
178
455
  active: [{sessionId: 'sessionId1'}],
@@ -180,8 +457,8 @@ describe('plugin-meetings', () => {
180
457
  allowed: [{sessionId: 'sessionId3'}],
181
458
  assignedCurrent: [{sessionId: 'sessionId4'}],
182
459
  requested: [{sessionId: 'sessionId5'}],
183
- }
184
- }
460
+ },
461
+ };
185
462
 
186
463
  breakouts.updateBreakoutSessions(payload);
187
464
 
@@ -190,7 +467,27 @@ describe('plugin-meetings', () => {
190
467
  checkBreakout(breakouts.breakouts.get('sessionId3'), 'sessionId3', 'allowed');
191
468
  checkBreakout(breakouts.breakouts.get('sessionId4'), 'sessionId4', 'assignedCurrent');
192
469
  checkBreakout(breakouts.breakouts.get('sessionId5'), 'sessionId5', 'requested');
193
- })
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
+ });
194
491
  });
195
492
 
196
493
  describe('#locusUrlUpdate', () => {
@@ -201,12 +498,24 @@ describe('plugin-meetings', () => {
201
498
  });
202
499
  });
203
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
+
204
513
  describe('#cleanUp', () => {
205
514
  it('stops listening', () => {
206
515
  breakouts.stopListening = sinon.stub();
207
-
516
+
208
517
  breakouts.cleanUp();
209
-
518
+
210
519
  assert.calledOnceWithExactly(breakouts.stopListening);
211
520
  });
212
521
  });
@@ -222,26 +531,145 @@ describe('plugin-meetings', () => {
222
531
  const breakout = breakouts.breakouts.models[0];
223
532
  breakout.parseRoster = sinon.stub();
224
533
 
225
- const locus = {controls: {breakout: {sessionId: 'sessionId'}}}
534
+ const locus = {controls: {breakout: {sessionId: 'sessionId'}}};
226
535
 
227
536
  breakouts.handleRosterUpdate(locus);
228
537
  assert.calledOnceWithExactly(breakout.parseRoster, locus);
229
538
  });
230
539
  });
231
540
 
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
+ });
550
+
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
+ });
568
+
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
+
232
657
  describe('#queryRosters', () => {
233
-
234
658
  it('makes the expected query', async () => {
235
-
236
- webex.request.returns(Promise.resolve({
237
- body: {
238
- rosters: [{
239
- locus: 'locus1'
240
- }, {
241
- locus: 'locus2'
242
- }]
243
- }
244
- }));
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
+ );
245
673
 
246
674
  breakouts.set('url', 'url');
247
675
  breakouts.set('locusUrl', 'test');
@@ -249,10 +677,10 @@ describe('plugin-meetings', () => {
249
677
  breakouts.handleRosterUpdate = sinon.stub();
250
678
 
251
679
  const result = await breakouts.queryRosters();
252
-
680
+
253
681
  assert.calledOnceWithExactly(webex.request, {
254
682
  uri: 'url/roster',
255
- qs: { locusUrl: 'dGVzdA==' }
683
+ qs: {locusUrl: 'dGVzdA=='},
256
684
  });
257
685
  assert.calledTwice(breakouts.handleRosterUpdate);
258
686
 
@@ -261,7 +689,7 @@ describe('plugin-meetings', () => {
261
689
  });
262
690
 
263
691
  it('logs the error if the query fails', async () => {
264
- const error = new Error('something went wrong')
692
+ const error = new Error('something went wrong');
265
693
  webex.request.rejects(error);
266
694
  LoggerProxy.logger.error = sinon.stub();
267
695
 
@@ -288,11 +716,88 @@ describe('plugin-meetings', () => {
288
716
  describe('isInMainSession', () => {
289
717
  it('returns true when sessionType is MAIN', () => {
290
718
  assert.equal(breakouts.isInMainSession, false);
291
- breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN)
719
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
292
720
  assert.equal(breakouts.isInMainSession, true);
293
721
  });
294
722
  });
295
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
+
740
+ describe('#getMainSession', () => {
741
+ it('returns main session as expect', () => {
742
+ breakouts.updateBreakout({
743
+ sessionId: 'sessionId',
744
+ groupId: 'groupId',
745
+ sessionType: 'sessionType',
746
+ url: 'url',
747
+ name: 'name',
748
+ allowBackToMain: true,
749
+ delayCloseTime: 10,
750
+ enableBreakoutSession: true,
751
+ startTime: 'startTime',
752
+ status: 'active',
753
+ locusUrl: 'locusUrl',
754
+ });
755
+ const payload = {
756
+ breakoutSessions: {
757
+ active: [{sessionId: 'sessionId1'}],
758
+ },
759
+ };
760
+
761
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
762
+ breakouts.updateBreakoutSessions(payload);
763
+ let result = breakouts.getMainSession();
764
+ assert.equal(result.sessionId, 'sessionId');
765
+
766
+ const payload2 = {
767
+ breakoutSessions: {
768
+ active: [{sessionId: 'sessionId1', sessionType: BREAKOUTS.SESSION_TYPES.MAIN}],
769
+ },
770
+ };
771
+ breakouts.updateBreakoutSessions(payload2);
772
+ breakouts.set('sessionType', 'BREAKOUT');
773
+ result = breakouts.getMainSession();
774
+ assert.equal(result.sessionId, 'sessionId1');
775
+ });
776
+ it('throw error if cannot find main session', () => {
777
+ const fn = () => {
778
+ breakouts.getMainSession();
779
+ };
780
+ expect(fn).to.throw(/no main session found/);
781
+ });
782
+ });
783
+
784
+ describe('#askAllToReturn', () => {
785
+ it('makes the request as expected', async () => {
786
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
787
+ breakouts.currentBreakoutSession.sessionId = 'sessionId';
788
+ breakouts.currentBreakoutSession.groupId = 'groupId';
789
+ const result = await breakouts.askAllToReturn();
790
+ assert.calledOnceWithExactly(webex.request, {
791
+ method: 'POST',
792
+ uri: 'url/requestMove',
793
+ body: {
794
+ groupId: 'groupId',
795
+ sessionId: 'sessionId',
796
+ },
797
+ });
798
+ });
799
+ });
800
+
296
801
  describe('#breakoutServiceUrlUpdate', () => {
297
802
  it('sets the breakoutService url', () => {
298
803
  breakouts.breakoutServiceUrlUpdate('newBreakoutServiceUrl');
@@ -301,19 +806,22 @@ describe('plugin-meetings', () => {
301
806
  });
302
807
 
303
808
  describe('#toggleBreakout', () => {
304
- it('enableBreakoutSession is undefined, run enableBreakouts then toggleBreakout', async() => {
305
- breakouts.enableBreakoutSession = undefined;
306
- breakouts.enableBreakouts = sinon.stub().resolves(({body: {
809
+ const mockEnableResponse = {
810
+ body: {
307
811
  sessionId: 'sessionId',
308
812
  groupId: 'groupId',
309
813
  name: 'name',
310
814
  current: true,
311
815
  sessionType: 'sessionType',
312
- url: 'url'
313
- }}))
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);
314
822
  breakouts.updateBreakout = sinon.stub().resolves();
315
823
  breakouts.doToggleBreakout = sinon.stub().resolves();
316
-
824
+
317
825
  await breakouts.toggleBreakout(false);
318
826
  assert.calledOnceWithExactly(breakouts.enableBreakouts);
319
827
  assert.calledOnceWithExactly(breakouts.updateBreakout, {
@@ -322,12 +830,31 @@ describe('plugin-meetings', () => {
322
830
  name: 'name',
323
831
  current: true,
324
832
  sessionType: 'sessionType',
325
- url: 'url'
833
+ url: 'url',
326
834
  });
327
835
  assert.calledOnceWithExactly(breakouts.doToggleBreakout, false);
328
836
  });
329
837
 
330
- 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 () => {
331
858
  breakouts.enableBreakoutSession = true;
332
859
  breakouts.doToggleBreakout = sinon.stub().resolves();
333
860
  await breakouts.toggleBreakout(true);
@@ -343,26 +870,920 @@ describe('plugin-meetings', () => {
343
870
  method: 'POST',
344
871
  uri: 'breakoutServiceUrl',
345
872
  body: {
346
- locusUrl: 'locusUrl'
347
- }
873
+ locusUrl: 'locusUrl',
874
+ },
348
875
  });
349
876
 
350
877
  assert.equal(result, 'REQUEST_RETURN_VALUE');
351
878
  });
352
879
  });
353
880
 
354
- describe('doToggleBreakout', () => {
881
+ describe('#broadcast', () => {
355
882
  it('makes the request as expected', async () => {
356
- const result = await breakouts.doToggleBreakout(true);
357
- assert.calledOnceWithExactly(webex.request, {
358
- method: 'PUT',
359
- uri: 'url',
360
- body: {
361
- enableBreakoutSession: true
362
- }
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();
892
+ let result = await breakouts.broadcast('hello');
893
+ assert.calledWithExactly(breakouts.breakoutRequest.broadcast, {
894
+ url: 'url',
895
+ message: 'hello',
896
+ options: undefined,
897
+ groupId: 'groupId',
363
898
  });
364
899
 
365
900
  assert.equal(result, 'REQUEST_RETURN_VALUE');
901
+
902
+ result = await breakouts.broadcast('hello', {presenters: true, cohosts: true});
903
+ assert.calledWithExactly(breakouts.breakoutRequest.broadcast, {
904
+ url: 'url',
905
+ groupId: 'groupId',
906
+ message: 'hello',
907
+ options: {presenters: true, cohosts: true},
908
+ });
909
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
910
+ });
911
+
912
+ it('throw error if no breakout group id found', () => {
913
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
914
+ const fn = () => {
915
+ breakouts.broadcast('hello');
916
+ };
917
+ expect(fn).to.throw(/no breakout session found/);
918
+ });
919
+ });
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
+
1227
+ describe('doToggleBreakout', () => {
1228
+ it('makes the request as expected', async () => {
1229
+ breakouts.set('editLock', {
1230
+ ttl: 30,
1231
+ token: 'editToken',
1232
+ state: 'UNLOCKED',
1233
+ });
1234
+ const result = await breakouts.doToggleBreakout(true);
1235
+ assert.calledOnceWithExactly(webex.request, {
1236
+ method: 'PUT',
1237
+ uri: 'url',
1238
+ body: {
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==',
1685
+ }
1686
+ });
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);
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})
366
1787
  });
367
1788
  });
368
1789
  });