@webex/plugin-meetings 3.0.0-beta.26 → 3.0.0-beta.261

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 (361) 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 +841 -19
  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/webex-errors.js +28 -7
  21. package/dist/common/errors/webex-errors.js.map +1 -1
  22. package/dist/common/logs/logger-proxy.js +1 -1
  23. package/dist/common/logs/logger-proxy.js.map +1 -1
  24. package/dist/common/queue.js +24 -9
  25. package/dist/common/queue.js.map +1 -1
  26. package/dist/config.js +5 -10
  27. package/dist/config.js.map +1 -1
  28. package/dist/constants.js +196 -28
  29. package/dist/constants.js.map +1 -1
  30. package/dist/controls-options-manager/enums.js +14 -2
  31. package/dist/controls-options-manager/enums.js.map +1 -1
  32. package/dist/controls-options-manager/index.js +109 -15
  33. package/dist/controls-options-manager/index.js.map +1 -1
  34. package/dist/controls-options-manager/types.js +7 -0
  35. package/dist/controls-options-manager/types.js.map +1 -0
  36. package/dist/controls-options-manager/util.js +309 -18
  37. package/dist/controls-options-manager/util.js.map +1 -1
  38. package/dist/index.js +112 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/interpretation/collection.js +23 -0
  41. package/dist/interpretation/collection.js.map +1 -0
  42. package/dist/interpretation/index.js +366 -0
  43. package/dist/interpretation/index.js.map +1 -0
  44. package/dist/interpretation/siLanguage.js +25 -0
  45. package/dist/interpretation/siLanguage.js.map +1 -0
  46. package/dist/locus-info/controlsUtils.js +91 -2
  47. package/dist/locus-info/controlsUtils.js.map +1 -1
  48. package/dist/locus-info/index.js +381 -62
  49. package/dist/locus-info/index.js.map +1 -1
  50. package/dist/locus-info/infoUtils.js +7 -1
  51. package/dist/locus-info/infoUtils.js.map +1 -1
  52. package/dist/locus-info/mediaSharesUtils.js +43 -1
  53. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  54. package/dist/locus-info/parser.js +224 -63
  55. package/dist/locus-info/parser.js.map +1 -1
  56. package/dist/locus-info/selfUtils.js +89 -14
  57. package/dist/locus-info/selfUtils.js.map +1 -1
  58. package/dist/media/index.js +58 -116
  59. package/dist/media/index.js.map +1 -1
  60. package/dist/media/properties.js +60 -121
  61. package/dist/media/properties.js.map +1 -1
  62. package/dist/meeting/in-meeting-actions.js +82 -2
  63. package/dist/meeting/in-meeting-actions.js.map +1 -1
  64. package/dist/meeting/index.js +3022 -2795
  65. package/dist/meeting/index.js.map +1 -1
  66. package/dist/meeting/locusMediaRequest.js +292 -0
  67. package/dist/meeting/locusMediaRequest.js.map +1 -0
  68. package/dist/meeting/muteState.js +230 -124
  69. package/dist/meeting/muteState.js.map +1 -1
  70. package/dist/meeting/request.js +256 -196
  71. package/dist/meeting/request.js.map +1 -1
  72. package/dist/meeting/util.js +601 -417
  73. package/dist/meeting/util.js.map +1 -1
  74. package/dist/meeting-info/index.js +70 -7
  75. package/dist/meeting-info/index.js.map +1 -1
  76. package/dist/meeting-info/meeting-info-v2.js +189 -51
  77. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  78. package/dist/meeting-info/util.js +1 -1
  79. package/dist/meeting-info/util.js.map +1 -1
  80. package/dist/meeting-info/utilv2.js +36 -36
  81. package/dist/meeting-info/utilv2.js.map +1 -1
  82. package/dist/meetings/collection.js +22 -0
  83. package/dist/meetings/collection.js.map +1 -1
  84. package/dist/meetings/index.js +372 -90
  85. package/dist/meetings/index.js.map +1 -1
  86. package/dist/meetings/meetings.types.js +7 -0
  87. package/dist/meetings/meetings.types.js.map +1 -0
  88. package/dist/meetings/request.js +2 -0
  89. package/dist/meetings/request.js.map +1 -1
  90. package/dist/meetings/util.js +88 -1
  91. package/dist/meetings/util.js.map +1 -1
  92. package/dist/member/index.js +49 -0
  93. package/dist/member/index.js.map +1 -1
  94. package/dist/member/types.js +25 -0
  95. package/dist/member/types.js.map +1 -0
  96. package/dist/member/util.js +121 -25
  97. package/dist/member/util.js.map +1 -1
  98. package/dist/members/collection.js +10 -0
  99. package/dist/members/collection.js.map +1 -1
  100. package/dist/members/index.js +86 -5
  101. package/dist/members/index.js.map +1 -1
  102. package/dist/members/request.js +106 -38
  103. package/dist/members/request.js.map +1 -1
  104. package/dist/members/types.js +15 -0
  105. package/dist/members/types.js.map +1 -0
  106. package/dist/members/util.js +316 -233
  107. package/dist/members/util.js.map +1 -1
  108. package/dist/metrics/constants.js +10 -5
  109. package/dist/metrics/constants.js.map +1 -1
  110. package/dist/metrics/index.js +1 -468
  111. package/dist/metrics/index.js.map +1 -1
  112. package/dist/multistream/mediaRequestManager.js +238 -49
  113. package/dist/multistream/mediaRequestManager.js.map +1 -1
  114. package/dist/multistream/receiveSlot.js +49 -16
  115. package/dist/multistream/receiveSlot.js.map +1 -1
  116. package/dist/multistream/receiveSlotManager.js +52 -34
  117. package/dist/multistream/receiveSlotManager.js.map +1 -1
  118. package/dist/multistream/remoteMedia.js +44 -18
  119. package/dist/multistream/remoteMedia.js.map +1 -1
  120. package/dist/multistream/remoteMediaGroup.js +60 -3
  121. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  122. package/dist/multistream/remoteMediaManager.js +209 -59
  123. package/dist/multistream/remoteMediaManager.js.map +1 -1
  124. package/dist/multistream/sendSlotManager.js +233 -0
  125. package/dist/multistream/sendSlotManager.js.map +1 -0
  126. package/dist/reachability/index.js +161 -57
  127. package/dist/reachability/index.js.map +1 -1
  128. package/dist/reachability/request.js +17 -8
  129. package/dist/reachability/request.js.map +1 -1
  130. package/dist/reconnection-manager/index.js +199 -154
  131. package/dist/reconnection-manager/index.js.map +1 -1
  132. package/dist/recording-controller/index.js +21 -2
  133. package/dist/recording-controller/index.js.map +1 -1
  134. package/dist/recording-controller/util.js +9 -8
  135. package/dist/recording-controller/util.js.map +1 -1
  136. package/dist/roap/index.js +23 -29
  137. package/dist/roap/index.js.map +1 -1
  138. package/dist/roap/request.js +112 -97
  139. package/dist/roap/request.js.map +1 -1
  140. package/dist/roap/turnDiscovery.js +96 -36
  141. package/dist/roap/turnDiscovery.js.map +1 -1
  142. package/dist/rtcMetrics/constants.js +12 -0
  143. package/dist/rtcMetrics/constants.js.map +1 -0
  144. package/dist/rtcMetrics/index.js +117 -0
  145. package/dist/rtcMetrics/index.js.map +1 -0
  146. package/dist/statsAnalyzer/index.js +51 -34
  147. package/dist/statsAnalyzer/index.js.map +1 -1
  148. package/dist/statsAnalyzer/mqaUtil.js +6 -6
  149. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  150. package/dist/types/annotation/annotation.types.d.ts +42 -0
  151. package/dist/types/annotation/constants.d.ts +31 -0
  152. package/dist/types/annotation/index.d.ts +117 -0
  153. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  154. package/dist/types/breakouts/events.d.ts +8 -0
  155. package/dist/types/breakouts/request.d.ts +22 -0
  156. package/dist/types/breakouts/utils.d.ts +15 -0
  157. package/dist/types/common/errors/webex-errors.d.ts +13 -1
  158. package/dist/types/common/queue.d.ts +9 -7
  159. package/dist/types/config.d.ts +1 -6
  160. package/dist/types/constants.d.ts +155 -21
  161. package/dist/types/controls-options-manager/enums.d.ts +11 -1
  162. package/dist/types/controls-options-manager/index.d.ts +17 -1
  163. package/dist/types/controls-options-manager/types.d.ts +43 -0
  164. package/dist/types/controls-options-manager/util.d.ts +1 -7
  165. package/dist/types/index.d.ts +6 -4
  166. package/dist/types/interpretation/collection.d.ts +5 -0
  167. package/dist/types/interpretation/index.d.ts +5 -0
  168. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  169. package/dist/types/locus-info/index.d.ts +57 -4
  170. package/dist/types/locus-info/parser.d.ts +65 -6
  171. package/dist/types/media/index.d.ts +2 -0
  172. package/dist/types/media/properties.d.ts +34 -48
  173. package/dist/types/meeting/in-meeting-actions.d.ts +82 -2
  174. package/dist/types/meeting/index.d.ts +344 -506
  175. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  176. package/dist/types/meeting/muteState.d.ts +99 -23
  177. package/dist/types/meeting/request.d.ts +72 -43
  178. package/dist/types/meeting/util.d.ts +101 -1
  179. package/dist/types/meeting-info/index.d.ts +13 -1
  180. package/dist/types/meeting-info/meeting-info-v2.d.ts +31 -1
  181. package/dist/types/meetings/collection.d.ts +8 -0
  182. package/dist/types/meetings/index.d.ts +86 -12
  183. package/dist/types/meetings/meetings.types.d.ts +4 -0
  184. package/dist/types/member/index.d.ts +13 -0
  185. package/dist/types/member/types.d.ts +32 -0
  186. package/dist/types/members/collection.d.ts +5 -0
  187. package/dist/types/members/index.d.ts +35 -2
  188. package/dist/types/members/request.d.ts +73 -9
  189. package/dist/types/members/types.d.ts +24 -0
  190. package/dist/types/members/util.d.ts +209 -1
  191. package/dist/types/metrics/constants.d.ts +9 -4
  192. package/dist/types/metrics/index.d.ts +4 -119
  193. package/dist/types/multistream/mediaRequestManager.d.ts +73 -5
  194. package/dist/types/multistream/receiveSlot.d.ts +16 -12
  195. package/dist/types/multistream/receiveSlotManager.d.ts +19 -4
  196. package/dist/types/multistream/remoteMedia.d.ts +8 -29
  197. package/dist/types/multistream/remoteMediaGroup.d.ts +0 -9
  198. package/dist/types/multistream/remoteMediaManager.d.ts +46 -2
  199. package/dist/types/multistream/sendSlotManager.d.ts +61 -0
  200. package/dist/types/reachability/index.d.ts +44 -7
  201. package/dist/types/reachability/request.d.ts +7 -3
  202. package/dist/types/reconnection-manager/index.d.ts +9 -0
  203. package/dist/types/recording-controller/index.d.ts +15 -1
  204. package/dist/types/recording-controller/util.d.ts +5 -4
  205. package/dist/types/roap/request.d.ts +15 -11
  206. package/dist/types/roap/turnDiscovery.d.ts +18 -1
  207. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  208. package/dist/types/rtcMetrics/index.d.ts +47 -0
  209. package/dist/types/statsAnalyzer/index.d.ts +6 -1
  210. package/package.json +23 -20
  211. package/src/annotation/annotation.types.ts +50 -0
  212. package/src/annotation/constants.ts +36 -0
  213. package/src/annotation/index.ts +328 -0
  214. package/src/breakouts/README.md +44 -14
  215. package/src/breakouts/breakout.ts +87 -9
  216. package/src/breakouts/edit-lock-error.ts +25 -0
  217. package/src/breakouts/events.ts +56 -0
  218. package/src/breakouts/index.ts +710 -10
  219. package/src/breakouts/request.ts +55 -0
  220. package/src/breakouts/utils.ts +57 -0
  221. package/src/common/errors/webex-errors.ts +27 -2
  222. package/src/common/logs/logger-proxy.ts +1 -1
  223. package/src/common/queue.ts +22 -8
  224. package/src/config.ts +4 -9
  225. package/src/constants.ts +178 -18
  226. package/src/controls-options-manager/enums.ts +12 -0
  227. package/src/controls-options-manager/index.ts +116 -21
  228. package/src/controls-options-manager/types.ts +59 -0
  229. package/src/controls-options-manager/util.ts +294 -14
  230. package/src/index.ts +40 -0
  231. package/src/interpretation/README.md +60 -0
  232. package/src/interpretation/collection.ts +19 -0
  233. package/src/interpretation/index.ts +332 -0
  234. package/src/interpretation/siLanguage.ts +18 -0
  235. package/src/locus-info/controlsUtils.ts +108 -0
  236. package/src/locus-info/index.ts +412 -59
  237. package/src/locus-info/infoUtils.ts +10 -2
  238. package/src/locus-info/mediaSharesUtils.ts +48 -0
  239. package/src/locus-info/parser.ts +231 -39
  240. package/src/locus-info/selfUtils.ts +81 -5
  241. package/src/media/index.ts +100 -122
  242. package/src/media/properties.ts +70 -108
  243. package/src/meeting/in-meeting-actions.ts +163 -3
  244. package/src/meeting/index.ts +2471 -2306
  245. package/src/meeting/locusMediaRequest.ts +313 -0
  246. package/src/meeting/muteState.ts +229 -131
  247. package/src/meeting/request.ts +172 -121
  248. package/src/meeting/util.ts +588 -394
  249. package/src/meeting-info/index.ts +79 -8
  250. package/src/meeting-info/meeting-info-v2.ts +168 -14
  251. package/src/meeting-info/util.ts +1 -1
  252. package/src/meeting-info/utilv2.ts +23 -23
  253. package/src/meetings/collection.ts +20 -0
  254. package/src/meetings/index.ts +414 -108
  255. package/src/meetings/meetings.types.ts +12 -0
  256. package/src/meetings/request.ts +2 -0
  257. package/src/meetings/util.ts +103 -4
  258. package/src/member/index.ts +49 -0
  259. package/src/member/types.ts +38 -0
  260. package/src/member/util.ts +127 -25
  261. package/src/members/collection.ts +8 -0
  262. package/src/members/index.ts +107 -6
  263. package/src/members/request.ts +97 -17
  264. package/src/members/types.ts +28 -0
  265. package/src/members/util.ts +319 -240
  266. package/src/metrics/constants.ts +9 -4
  267. package/src/metrics/index.ts +1 -490
  268. package/src/multistream/mediaRequestManager.ts +289 -79
  269. package/src/multistream/receiveSlot.ts +55 -18
  270. package/src/multistream/receiveSlotManager.ts +46 -24
  271. package/src/multistream/remoteMedia.ts +27 -2
  272. package/src/multistream/remoteMediaGroup.ts +59 -0
  273. package/src/multistream/remoteMediaManager.ts +148 -30
  274. package/src/multistream/sendSlotManager.ts +170 -0
  275. package/src/reachability/index.ts +165 -37
  276. package/src/reachability/request.ts +17 -8
  277. package/src/reconnection-manager/index.ts +81 -54
  278. package/src/recording-controller/index.ts +20 -3
  279. package/src/recording-controller/util.ts +26 -9
  280. package/src/roap/index.ts +23 -30
  281. package/src/roap/request.ts +100 -104
  282. package/src/roap/turnDiscovery.ts +51 -25
  283. package/src/rtcMetrics/constants.ts +3 -0
  284. package/src/rtcMetrics/index.ts +100 -0
  285. package/src/statsAnalyzer/index.ts +73 -35
  286. package/src/statsAnalyzer/mqaUtil.ts +8 -10
  287. package/test/integration/spec/converged-space-meetings.js +233 -0
  288. package/test/integration/spec/journey.js +320 -261
  289. package/test/integration/spec/space-meeting.js +76 -3
  290. package/test/unit/spec/annotation/index.ts +418 -0
  291. package/test/unit/spec/breakouts/breakout.ts +142 -24
  292. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  293. package/test/unit/spec/breakouts/events.ts +89 -0
  294. package/test/unit/spec/breakouts/index.ts +1545 -48
  295. package/test/unit/spec/breakouts/request.ts +104 -0
  296. package/test/unit/spec/breakouts/utils.js +72 -0
  297. package/test/unit/spec/common/queue.js +31 -2
  298. package/test/unit/spec/controls-options-manager/index.js +163 -0
  299. package/test/unit/spec/controls-options-manager/util.js +576 -60
  300. package/test/unit/spec/fixture/locus.js +1 -0
  301. package/test/unit/spec/interpretation/collection.ts +15 -0
  302. package/test/unit/spec/interpretation/index.ts +589 -0
  303. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  304. package/test/unit/spec/locus-info/controlsUtils.js +316 -43
  305. package/test/unit/spec/locus-info/index.js +1283 -33
  306. package/test/unit/spec/locus-info/infoUtils.js +37 -15
  307. package/test/unit/spec/locus-info/mediaSharesUtils.ts +22 -0
  308. package/test/unit/spec/locus-info/parser.js +62 -22
  309. package/test/unit/spec/locus-info/selfConstant.js +27 -4
  310. package/test/unit/spec/locus-info/selfUtils.js +208 -17
  311. package/test/unit/spec/media/index.ts +104 -37
  312. package/test/unit/spec/meeting/in-meeting-actions.ts +81 -3
  313. package/test/unit/spec/meeting/index.js +4095 -1913
  314. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  315. package/test/unit/spec/meeting/muteState.js +408 -208
  316. package/test/unit/spec/meeting/request.js +440 -45
  317. package/test/unit/spec/meeting/utils.js +679 -64
  318. package/test/unit/spec/meeting-info/index.js +293 -0
  319. package/test/unit/spec/meeting-info/meetinginfov2.js +517 -5
  320. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  321. package/test/unit/spec/meetings/collection.js +14 -0
  322. package/test/unit/spec/meetings/index.js +941 -151
  323. package/test/unit/spec/meetings/utils.js +206 -2
  324. package/test/unit/spec/member/index.js +58 -4
  325. package/test/unit/spec/member/util.js +479 -35
  326. package/test/unit/spec/members/index.js +319 -1
  327. package/test/unit/spec/members/request.js +206 -27
  328. package/test/unit/spec/members/utils.js +184 -0
  329. package/test/unit/spec/metrics/index.js +1 -50
  330. package/test/unit/spec/multistream/mediaRequestManager.ts +803 -162
  331. package/test/unit/spec/multistream/receiveSlot.ts +72 -13
  332. package/test/unit/spec/multistream/receiveSlotManager.ts +58 -28
  333. package/test/unit/spec/multistream/remoteMedia.ts +30 -0
  334. package/test/unit/spec/multistream/remoteMediaGroup.ts +266 -0
  335. package/test/unit/spec/multistream/remoteMediaManager.ts +326 -0
  336. package/test/unit/spec/multistream/sendSlotManager.ts +242 -0
  337. package/test/unit/spec/reachability/index.ts +343 -9
  338. package/test/unit/spec/reachability/request.js +68 -0
  339. package/test/unit/spec/reconnection-manager/index.js +84 -9
  340. package/test/unit/spec/recording-controller/index.js +294 -218
  341. package/test/unit/spec/recording-controller/util.js +223 -96
  342. package/test/unit/spec/roap/index.ts +31 -51
  343. package/test/unit/spec/roap/request.ts +203 -85
  344. package/test/unit/spec/roap/turnDiscovery.ts +48 -13
  345. package/test/unit/spec/rtcMetrics/index.ts +68 -0
  346. package/test/unit/spec/stats-analyzer/index.js +29 -2
  347. package/test/utils/constants.js +9 -0
  348. package/test/utils/integrationTestUtils.js +46 -0
  349. package/test/utils/testUtils.js +0 -45
  350. package/test/utils/webex-config.js +4 -0
  351. package/test/utils/webex-test-users.js +6 -3
  352. package/dist/meeting/effectsState.js +0 -262
  353. package/dist/meeting/effectsState.js.map +0 -1
  354. package/dist/metrics/config.js +0 -299
  355. package/dist/metrics/config.js.map +0 -1
  356. package/dist/types/meeting/effectsState.d.ts +0 -42
  357. package/dist/types/metrics/config.d.ts +0 -178
  358. package/src/index.js +0 -16
  359. package/src/meeting/effectsState.ts +0 -211
  360. package/src/metrics/config.ts +0 -495
  361. 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,7 +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';
95
+ breakouts.locusUrl = 'locusUrl';
96
+ breakouts.breakoutServiceUrl = 'breakoutServiceUrl';
23
97
  webex.request = sinon.stub().returns(Promise.resolve('REQUEST_RETURN_VALUE'));
98
+ webex.meetings = {};
99
+ webex.meetings.getMeetingByType = sinon.stub();
24
100
  });
25
101
 
26
102
  describe('#initialize', () => {
@@ -28,19 +104,59 @@ describe('plugin-meetings', () => {
28
104
  assert.equal(breakouts.namespace, 'Meetings');
29
105
  });
30
106
 
31
- it('emits BREAKOUTS_CLOSING event when the status is CLOSING', () => {
32
- let called = false;
33
- breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.BREAKOUTS_CLOSING, () => {
34
- 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
35
139
  });
36
140
 
37
- 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
+ });
38
149
 
39
- assert.isFalse(called);
150
+ const breakoutClosingHandler = sinon.stub();
151
+ breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.BREAKOUTS_CLOSING, breakoutClosingHandler);
40
152
 
41
- 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
+ });
42
158
 
43
- assert.isTrue(called);
159
+ assert.notCalled(breakoutClosingHandler);
44
160
  });
45
161
 
46
162
  it('debounces querying rosters on add', () => {
@@ -49,20 +165,78 @@ describe('plugin-meetings', () => {
49
165
 
50
166
  assert.calledOnceWithExactly(breakouts.debouncedQueryRosters);
51
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
+ });
52
226
  });
53
227
 
54
228
  describe('#listenToBroadcastMessages', () => {
55
229
  it('triggers message event when a message received', () => {
56
230
  const call = webex.internal.llm.on.getCall(0);
57
231
  const callback = call.args[1];
58
-
232
+
59
233
  assert.equal(call.args[0], 'event:breakout.message');
60
234
 
61
235
  let message;
62
236
 
63
237
  breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.MESSAGE, (event) => {
64
238
  message = event;
65
- })
239
+ });
66
240
 
67
241
  breakouts.currentBreakoutSession.sessionId = 'sessionId';
68
242
 
@@ -71,14 +245,14 @@ describe('plugin-meetings', () => {
71
245
  senderUserId: 'senderUserId',
72
246
  sentTime: 'sentTime',
73
247
  message: 'message',
74
- }
248
+ },
75
249
  });
76
250
 
77
251
  assert.deepEqual(message, {
78
- senderUserId: "senderUserId",
252
+ senderUserId: 'senderUserId',
79
253
  sentTime: 'sentTime',
80
254
  message: 'message',
81
- sessionId: 'sessionId'
255
+ sessionId: 'sessionId',
82
256
  });
83
257
  });
84
258
  });
@@ -87,20 +261,20 @@ describe('plugin-meetings', () => {
87
261
  it('triggers member update event when a roster received', () => {
88
262
  const call = webex.internal.mercury.on.getCall(0);
89
263
  const callback = call.args[1];
90
-
264
+
91
265
  assert.equal(call.args[0], 'event:breakout.roster');
92
266
 
93
267
  let called = false;
94
268
 
95
269
  breakouts.listenTo(breakouts, BREAKOUTS.EVENTS.MEMBERS_UPDATE, () => {
96
270
  called = true;
97
- })
271
+ });
98
272
  breakouts.handleRosterUpdate = sinon.stub();
99
273
 
100
274
  callback({
101
275
  data: {
102
- locus: 'locus'
103
- }
276
+ locus: 'locus',
277
+ },
104
278
  });
105
279
 
106
280
  assert.isTrue(called);
@@ -108,6 +282,33 @@ describe('plugin-meetings', () => {
108
282
  });
109
283
  });
110
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
+
111
312
  describe('#updateBreakout', () => {
112
313
  it('updates the current breakout session', () => {
113
314
  breakouts.updateBreakout({
@@ -121,7 +322,7 @@ describe('plugin-meetings', () => {
121
322
  enableBreakoutSession: true,
122
323
  startTime: 'startTime',
123
324
  status: 'active',
124
- locusUrl: 'locusUrl'
325
+ locusUrl: 'locusUrl',
125
326
  });
126
327
 
127
328
  assert.equal(breakouts.allowBackToMain, true);
@@ -147,10 +348,90 @@ describe('plugin-meetings', () => {
147
348
  assert.equal(breakouts.currentBreakoutSession.assignedCurrent, false);
148
349
  assert.equal(breakouts.currentBreakoutSession.requested, false);
149
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
+ });
150
432
  });
151
433
 
152
434
  describe('#updateBreakoutSessions', () => {
153
-
154
435
  const checkBreakout = (breakout, sessionId, state) => {
155
436
  assert.deepEqual(breakout.attributes, {
156
437
  active: false,
@@ -162,14 +443,13 @@ describe('plugin-meetings', () => {
162
443
  requested: false,
163
444
  url: 'url',
164
445
  sessionId,
165
- ...{[state]: true}
446
+ ...{[state]: true},
166
447
  });
167
- }
448
+ };
168
449
 
169
450
  it('works', () => {
170
-
171
451
  breakouts.set('url', 'url');
172
-
452
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
173
453
  const payload = {
174
454
  breakoutSessions: {
175
455
  active: [{sessionId: 'sessionId1'}],
@@ -177,8 +457,8 @@ describe('plugin-meetings', () => {
177
457
  allowed: [{sessionId: 'sessionId3'}],
178
458
  assignedCurrent: [{sessionId: 'sessionId4'}],
179
459
  requested: [{sessionId: 'sessionId5'}],
180
- }
181
- }
460
+ },
461
+ };
182
462
 
183
463
  breakouts.updateBreakoutSessions(payload);
184
464
 
@@ -187,7 +467,27 @@ describe('plugin-meetings', () => {
187
467
  checkBreakout(breakouts.breakouts.get('sessionId3'), 'sessionId3', 'allowed');
188
468
  checkBreakout(breakouts.breakouts.get('sessionId4'), 'sessionId4', 'assignedCurrent');
189
469
  checkBreakout(breakouts.breakouts.get('sessionId5'), 'sessionId5', 'requested');
190
- })
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
+ });
191
491
  });
192
492
 
193
493
  describe('#locusUrlUpdate', () => {
@@ -198,12 +498,24 @@ describe('plugin-meetings', () => {
198
498
  });
199
499
  });
200
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
+
201
513
  describe('#cleanUp', () => {
202
514
  it('stops listening', () => {
203
515
  breakouts.stopListening = sinon.stub();
204
-
516
+
205
517
  breakouts.cleanUp();
206
-
518
+
207
519
  assert.calledOnceWithExactly(breakouts.stopListening);
208
520
  });
209
521
  });
@@ -219,26 +531,145 @@ describe('plugin-meetings', () => {
219
531
  const breakout = breakouts.breakouts.models[0];
220
532
  breakout.parseRoster = sinon.stub();
221
533
 
222
- const locus = {controls: {breakout: {sessionId: 'sessionId'}}}
534
+ const locus = {controls: {breakout: {sessionId: 'sessionId'}}};
223
535
 
224
536
  breakouts.handleRosterUpdate(locus);
225
537
  assert.calledOnceWithExactly(breakout.parseRoster, locus);
226
538
  });
227
539
  });
228
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
+
229
657
  describe('#queryRosters', () => {
230
-
231
658
  it('makes the expected query', async () => {
232
-
233
- webex.request.returns(Promise.resolve({
234
- body: {
235
- rosters: [{
236
- locus: 'locus1'
237
- }, {
238
- locus: 'locus2'
239
- }]
240
- }
241
- }));
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
+ );
242
673
 
243
674
  breakouts.set('url', 'url');
244
675
  breakouts.set('locusUrl', 'test');
@@ -246,10 +677,10 @@ describe('plugin-meetings', () => {
246
677
  breakouts.handleRosterUpdate = sinon.stub();
247
678
 
248
679
  const result = await breakouts.queryRosters();
249
-
680
+
250
681
  assert.calledOnceWithExactly(webex.request, {
251
682
  uri: 'url/roster',
252
- qs: { locusUrl: 'dGVzdA==' }
683
+ qs: {locusUrl: 'dGVzdA=='},
253
684
  });
254
685
  assert.calledTwice(breakouts.handleRosterUpdate);
255
686
 
@@ -258,7 +689,7 @@ describe('plugin-meetings', () => {
258
689
  });
259
690
 
260
691
  it('logs the error if the query fails', async () => {
261
- const error = new Error('something went wrong')
692
+ const error = new Error('something went wrong');
262
693
  webex.request.rejects(error);
263
694
  LoggerProxy.logger.error = sinon.stub();
264
695
 
@@ -285,9 +716,1075 @@ describe('plugin-meetings', () => {
285
716
  describe('isInMainSession', () => {
286
717
  it('returns true when sessionType is MAIN', () => {
287
718
  assert.equal(breakouts.isInMainSession, false);
288
- breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN)
719
+ breakouts.set('sessionType', BREAKOUTS.SESSION_TYPES.MAIN);
289
720
  assert.equal(breakouts.isInMainSession, true);
290
721
  });
291
722
  });
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
+
801
+ describe('#breakoutServiceUrlUpdate', () => {
802
+ it('sets the breakoutService url', () => {
803
+ breakouts.breakoutServiceUrlUpdate('newBreakoutServiceUrl');
804
+ assert.equal(breakouts.breakoutServiceUrl, 'newBreakoutServiceUrl/breakout/');
805
+ });
806
+ });
807
+
808
+ describe('#toggleBreakout', () => {
809
+ const mockEnableResponse = {
810
+ body: {
811
+ sessionId: 'sessionId',
812
+ groupId: 'groupId',
813
+ name: 'name',
814
+ current: true,
815
+ sessionType: 'sessionType',
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);
822
+ breakouts.updateBreakout = sinon.stub().resolves();
823
+ breakouts.doToggleBreakout = sinon.stub().resolves();
824
+
825
+ await breakouts.toggleBreakout(false);
826
+ assert.calledOnceWithExactly(breakouts.enableBreakouts);
827
+ assert.calledOnceWithExactly(breakouts.updateBreakout, {
828
+ sessionId: 'sessionId',
829
+ groupId: 'groupId',
830
+ name: 'name',
831
+ current: true,
832
+ sessionType: 'sessionType',
833
+ url: 'url',
834
+ });
835
+ assert.calledOnceWithExactly(breakouts.doToggleBreakout, false);
836
+ });
837
+
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 () => {
858
+ breakouts.enableBreakoutSession = true;
859
+ breakouts.doToggleBreakout = sinon.stub().resolves();
860
+ await breakouts.toggleBreakout(true);
861
+ assert.calledOnceWithExactly(breakouts.doToggleBreakout, true);
862
+ });
863
+ });
864
+
865
+ describe('enableBreakouts', () => {
866
+ it('makes the request as expected', async () => {
867
+ const result = await breakouts.enableBreakouts();
868
+ breakouts.set('breakoutServiceUrl', 'breakoutServiceUrl');
869
+ assert.calledOnceWithExactly(webex.request, {
870
+ method: 'POST',
871
+ uri: 'breakoutServiceUrl',
872
+ body: {
873
+ locusUrl: 'locusUrl',
874
+ },
875
+ });
876
+
877
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
878
+ });
879
+ });
880
+
881
+ describe('#broadcast', () => {
882
+ it('makes the request as expected', async () => {
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',
898
+ });
899
+
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})
1787
+ });
1788
+ });
292
1789
  });
293
1790
  });