@webex/plugin-meetings 3.0.0-beta.2 → 3.0.0-beta.20

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 (365) hide show
  1. package/UPGRADING.md +9 -9
  2. package/browsers.js +19 -24
  3. package/dist/breakouts/breakout.js +116 -0
  4. package/dist/breakouts/breakout.js.map +1 -0
  5. package/dist/breakouts/collection.js +23 -0
  6. package/dist/breakouts/collection.js.map +1 -0
  7. package/dist/breakouts/index.js +226 -0
  8. package/dist/breakouts/index.js.map +1 -0
  9. package/dist/common/browser-detection.js +1 -20
  10. package/dist/common/browser-detection.js.map +1 -1
  11. package/dist/common/collection.js +5 -20
  12. package/dist/common/collection.js.map +1 -1
  13. package/dist/common/config.js +0 -7
  14. package/dist/common/config.js.map +1 -1
  15. package/dist/common/errors/captcha-error.js +10 -24
  16. package/dist/common/errors/captcha-error.js.map +1 -1
  17. package/dist/common/errors/intent-to-join.js +11 -24
  18. package/dist/common/errors/intent-to-join.js.map +1 -1
  19. package/dist/common/errors/join-meeting.js +12 -25
  20. package/dist/common/errors/join-meeting.js.map +1 -1
  21. package/dist/common/errors/media.js +10 -24
  22. package/dist/common/errors/media.js.map +1 -1
  23. package/dist/common/errors/parameter.js +5 -33
  24. package/dist/common/errors/parameter.js.map +1 -1
  25. package/dist/common/errors/password-error.js +10 -24
  26. package/dist/common/errors/password-error.js.map +1 -1
  27. package/dist/common/errors/permission.js +9 -23
  28. package/dist/common/errors/permission.js.map +1 -1
  29. package/dist/common/errors/reconnection-in-progress.js +0 -17
  30. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  31. package/dist/common/errors/reconnection.js +10 -24
  32. package/dist/common/errors/reconnection.js.map +1 -1
  33. package/dist/common/errors/stats.js +10 -24
  34. package/dist/common/errors/stats.js.map +1 -1
  35. package/dist/common/errors/webex-errors.js +6 -41
  36. package/dist/common/errors/webex-errors.js.map +1 -1
  37. package/dist/common/errors/webex-meetings-error.js +5 -25
  38. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  39. package/dist/common/events/events-scope.js +0 -22
  40. package/dist/common/events/events-scope.js.map +1 -1
  41. package/dist/common/events/events.js +0 -23
  42. package/dist/common/events/events.js.map +1 -1
  43. package/dist/common/events/trigger-proxy.js +0 -12
  44. package/dist/common/events/trigger-proxy.js.map +1 -1
  45. package/dist/common/events/util.js +0 -15
  46. package/dist/common/events/util.js.map +1 -1
  47. package/dist/common/logs/logger-config.js +0 -4
  48. package/dist/common/logs/logger-config.js.map +1 -1
  49. package/dist/common/logs/logger-proxy.js +1 -8
  50. package/dist/common/logs/logger-proxy.js.map +1 -1
  51. package/dist/common/logs/request.js +37 -60
  52. package/dist/common/logs/request.js.map +1 -1
  53. package/dist/common/queue.js +4 -14
  54. package/dist/common/queue.js.map +1 -1
  55. package/dist/config.js +6 -6
  56. package/dist/config.js.map +1 -1
  57. package/dist/constants.js +88 -46
  58. package/dist/constants.js.map +1 -1
  59. package/dist/index.js +1 -17
  60. package/dist/index.js.map +1 -1
  61. package/dist/locus-info/controlsUtils.js +12 -29
  62. package/dist/locus-info/controlsUtils.js.map +1 -1
  63. package/dist/locus-info/embeddedAppsUtils.js +3 -26
  64. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  65. package/dist/locus-info/fullState.js +0 -15
  66. package/dist/locus-info/fullState.js.map +1 -1
  67. package/dist/locus-info/hostUtils.js +4 -12
  68. package/dist/locus-info/hostUtils.js.map +1 -1
  69. package/dist/locus-info/index.js +184 -190
  70. package/dist/locus-info/index.js.map +1 -1
  71. package/dist/locus-info/infoUtils.js +3 -37
  72. package/dist/locus-info/infoUtils.js.map +1 -1
  73. package/dist/locus-info/mediaSharesUtils.js +12 -38
  74. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  75. package/dist/locus-info/parser.js +92 -118
  76. package/dist/locus-info/parser.js.map +1 -1
  77. package/dist/locus-info/selfUtils.js +34 -91
  78. package/dist/locus-info/selfUtils.js.map +1 -1
  79. package/dist/media/index.js +67 -111
  80. package/dist/media/index.js.map +1 -1
  81. package/dist/media/properties.js +80 -114
  82. package/dist/media/properties.js.map +1 -1
  83. package/dist/media/util.js +2 -9
  84. package/dist/media/util.js.map +1 -1
  85. package/dist/mediaQualityMetrics/config.js +10 -12
  86. package/dist/mediaQualityMetrics/config.js.map +1 -1
  87. package/dist/meeting/effectsState.js +125 -190
  88. package/dist/meeting/effectsState.js.map +1 -1
  89. package/dist/meeting/in-meeting-actions.js +5 -14
  90. package/dist/meeting/in-meeting-actions.js.map +1 -1
  91. package/dist/meeting/index.js +1692 -1925
  92. package/dist/meeting/index.js.map +1 -1
  93. package/dist/meeting/muteState.js +36 -77
  94. package/dist/meeting/muteState.js.map +1 -1
  95. package/dist/meeting/request.js +224 -230
  96. package/dist/meeting/request.js.map +1 -1
  97. package/dist/meeting/request.type.js +7 -0
  98. package/dist/meeting/request.type.js.map +1 -0
  99. package/dist/meeting/state.js +21 -31
  100. package/dist/meeting/state.js.map +1 -1
  101. package/dist/meeting/util.js +43 -215
  102. package/dist/meeting/util.js.map +1 -1
  103. package/dist/meeting-info/collection.js +6 -25
  104. package/dist/meeting-info/collection.js.map +1 -1
  105. package/dist/meeting-info/index.js +14 -32
  106. package/dist/meeting-info/index.js.map +1 -1
  107. package/dist/meeting-info/meeting-info-v2.js +193 -268
  108. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  109. package/dist/meeting-info/request.js +3 -15
  110. package/dist/meeting-info/request.js.map +1 -1
  111. package/dist/meeting-info/util.js +98 -183
  112. package/dist/meeting-info/util.js.map +1 -1
  113. package/dist/meeting-info/utilv2.js +137 -228
  114. package/dist/meeting-info/utilv2.js.map +1 -1
  115. package/dist/meetings/collection.js +5 -20
  116. package/dist/meetings/collection.js.map +1 -1
  117. package/dist/meetings/index.js +490 -560
  118. package/dist/meetings/index.js.map +1 -1
  119. package/dist/meetings/request.js +24 -41
  120. package/dist/meetings/request.js.map +1 -1
  121. package/dist/meetings/util.js +99 -155
  122. package/dist/meetings/util.js.map +1 -1
  123. package/dist/member/index.js +78 -86
  124. package/dist/member/index.js.map +1 -1
  125. package/dist/member/util.js +31 -68
  126. package/dist/member/util.js.map +1 -1
  127. package/dist/members/collection.js +3 -12
  128. package/dist/members/collection.js.map +1 -1
  129. package/dist/members/index.js +93 -200
  130. package/dist/members/index.js.map +1 -1
  131. package/dist/members/request.js +16 -39
  132. package/dist/members/request.js.map +1 -1
  133. package/dist/members/util.js +9 -38
  134. package/dist/members/util.js.map +1 -1
  135. package/dist/metrics/config.js +0 -2
  136. package/dist/metrics/config.js.map +1 -1
  137. package/dist/metrics/constants.js +1 -2
  138. package/dist/metrics/constants.js.map +1 -1
  139. package/dist/metrics/index.js +55 -135
  140. package/dist/metrics/index.js.map +1 -1
  141. package/dist/multistream/mediaRequestManager.js +57 -32
  142. package/dist/multistream/mediaRequestManager.js.map +1 -1
  143. package/dist/multistream/multistreamMedia.js +15 -21
  144. package/dist/multistream/multistreamMedia.js.map +1 -1
  145. package/dist/multistream/receiveSlot.js +10 -50
  146. package/dist/multistream/receiveSlot.js.map +1 -1
  147. package/dist/multistream/receiveSlotManager.js +45 -82
  148. package/dist/multistream/receiveSlotManager.js.map +1 -1
  149. package/dist/multistream/remoteMedia.js +18 -58
  150. package/dist/multistream/remoteMedia.js.map +1 -1
  151. package/dist/multistream/remoteMediaGroup.js +6 -40
  152. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  153. package/dist/multistream/remoteMediaManager.js +362 -416
  154. package/dist/multistream/remoteMediaManager.js.map +1 -1
  155. package/dist/networkQualityMonitor/index.js +36 -57
  156. package/dist/networkQualityMonitor/index.js.map +1 -1
  157. package/dist/personal-meeting-room/index.js +21 -45
  158. package/dist/personal-meeting-room/index.js.map +1 -1
  159. package/dist/personal-meeting-room/request.js +1 -31
  160. package/dist/personal-meeting-room/request.js.map +1 -1
  161. package/dist/personal-meeting-room/util.js +0 -13
  162. package/dist/personal-meeting-room/util.js.map +1 -1
  163. package/dist/reachability/index.js +138 -182
  164. package/dist/reachability/index.js.map +1 -1
  165. package/dist/reachability/request.js +3 -18
  166. package/dist/reachability/request.js.map +1 -1
  167. package/dist/reactions/constants.js +13 -0
  168. package/dist/reactions/constants.js.map +1 -0
  169. package/dist/reactions/reactions.js +109 -0
  170. package/dist/reactions/reactions.js.map +1 -0
  171. package/dist/reactions/reactions.type.js +36 -0
  172. package/dist/reactions/reactions.type.js.map +1 -0
  173. package/dist/reconnection-manager/index.js +322 -455
  174. package/dist/reconnection-manager/index.js.map +1 -1
  175. package/dist/recording-controller/enums.js +17 -0
  176. package/dist/recording-controller/enums.js.map +1 -0
  177. package/dist/recording-controller/index.js +343 -0
  178. package/dist/recording-controller/index.js.map +1 -0
  179. package/dist/recording-controller/util.js +63 -0
  180. package/dist/recording-controller/util.js.map +1 -0
  181. package/dist/roap/index.js +39 -64
  182. package/dist/roap/index.js.map +1 -1
  183. package/dist/roap/request.js +94 -113
  184. package/dist/roap/request.js.map +1 -1
  185. package/dist/roap/turnDiscovery.js +85 -94
  186. package/dist/roap/turnDiscovery.js.map +1 -1
  187. package/dist/statsAnalyzer/global.js +0 -2
  188. package/dist/statsAnalyzer/global.js.map +1 -1
  189. package/dist/statsAnalyzer/index.js +85 -175
  190. package/dist/statsAnalyzer/index.js.map +1 -1
  191. package/dist/statsAnalyzer/mqaUtil.js +72 -53
  192. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  193. package/dist/transcription/index.js +22 -47
  194. package/dist/transcription/index.js.map +1 -1
  195. package/internal-README.md +7 -6
  196. package/package.json +25 -20
  197. package/src/breakouts/README.md +190 -0
  198. package/src/breakouts/breakout.ts +110 -0
  199. package/src/breakouts/collection.ts +19 -0
  200. package/src/breakouts/index.ts +225 -0
  201. package/src/common/{browser-detection.js → browser-detection.ts} +9 -6
  202. package/src/common/collection.ts +9 -7
  203. package/src/common/{config.js → config.ts} +1 -1
  204. package/src/common/errors/{captcha-error.js → captcha-error.ts} +11 -7
  205. package/src/common/errors/{intent-to-join.js → intent-to-join.ts} +12 -7
  206. package/src/common/errors/{join-meeting.js → join-meeting.ts} +17 -8
  207. package/src/common/errors/{media.js → media.ts} +11 -7
  208. package/src/common/errors/parameter.ts +11 -7
  209. package/src/common/errors/{password-error.js → password-error.ts} +11 -7
  210. package/src/common/errors/{permission.js → permission.ts} +10 -6
  211. package/src/common/errors/{reconnection-in-progress.js → reconnection-in-progress.ts} +0 -0
  212. package/src/common/errors/{reconnection.js → reconnection.ts} +11 -7
  213. package/src/common/errors/{stats.js → stats.ts} +11 -7
  214. package/src/common/errors/{webex-errors.js → webex-errors.ts} +8 -7
  215. package/src/common/errors/{webex-meetings-error.js → webex-meetings-error.ts} +4 -2
  216. package/src/common/events/{events-scope.js → events-scope.ts} +6 -2
  217. package/src/common/events/{events.js → events.ts} +5 -1
  218. package/src/common/events/{trigger-proxy.js → trigger-proxy.ts} +9 -5
  219. package/src/common/events/{util.js → util.ts} +2 -3
  220. package/src/common/logs/{logger-config.js → logger-config.ts} +1 -2
  221. package/src/common/logs/logger-proxy.ts +44 -0
  222. package/src/common/logs/{request.js → request.ts} +22 -9
  223. package/src/common/queue.ts +1 -2
  224. package/src/{config.js → config.ts} +17 -12
  225. package/src/constants.ts +40 -1
  226. package/src/index.js +1 -1
  227. package/src/locus-info/controlsUtils.ts +114 -0
  228. package/src/locus-info/{embeddedAppsUtils.js → embeddedAppsUtils.ts} +5 -6
  229. package/src/locus-info/{fullState.js → fullState.ts} +16 -12
  230. package/src/locus-info/{hostUtils.js → hostUtils.ts} +9 -8
  231. package/src/locus-info/{index.js → index.ts} +148 -64
  232. package/src/locus-info/{infoUtils.js → infoUtils.ts} +19 -8
  233. package/src/locus-info/{mediaSharesUtils.js → mediaSharesUtils.ts} +17 -17
  234. package/src/locus-info/{parser.js → parser.ts} +67 -79
  235. package/src/locus-info/{selfUtils.js → selfUtils.ts} +123 -68
  236. package/src/media/{index.js → index.ts} +181 -131
  237. package/src/media/{properties.js → properties.ts} +47 -28
  238. package/src/media/{util.js → util.ts} +2 -2
  239. package/src/mediaQualityMetrics/{config.js → config.ts} +46 -46
  240. package/src/meeting/{effectsState.js → effectsState.ts} +47 -41
  241. package/src/meeting/in-meeting-actions.ts +15 -3
  242. package/src/meeting/{index.js → index.ts} +2263 -1427
  243. package/src/meeting/{muteState.js → muteState.ts} +78 -42
  244. package/src/meeting/{request.js → request.ts} +292 -142
  245. package/src/meeting/request.type.ts +13 -0
  246. package/src/meeting/{state.js → state.ts} +50 -35
  247. package/src/meeting/{util.js → util.ts} +112 -115
  248. package/src/meeting-info/{collection.js → collection.ts} +6 -2
  249. package/src/meeting-info/{index.js → index.ts} +42 -36
  250. package/src/meeting-info/meeting-info-v2.ts +273 -0
  251. package/src/meeting-info/{request.js → request.ts} +14 -4
  252. package/src/meeting-info/{util.js → util.ts} +60 -51
  253. package/src/meeting-info/{utilv2.js → utilv2.ts} +65 -58
  254. package/src/meetings/{collection.js → collection.ts} +6 -3
  255. package/src/meetings/index.ts +1159 -0
  256. package/src/meetings/{request.js → request.ts} +32 -25
  257. package/src/meetings/{util.js → util.ts} +34 -32
  258. package/src/member/{index.js → index.ts} +102 -56
  259. package/src/member/{util.js → util.ts} +52 -25
  260. package/src/members/{collection.js → collection.ts} +2 -2
  261. package/src/members/{index.js → index.ts} +219 -142
  262. package/src/members/{request.js → request.ts} +60 -16
  263. package/src/members/{util.js → util.ts} +50 -48
  264. package/src/metrics/{config.js → config.ts} +254 -83
  265. package/src/metrics/{constants.js → constants.ts} +0 -2
  266. package/src/metrics/{index.js → index.ts} +106 -74
  267. package/src/multistream/mediaRequestManager.ts +81 -15
  268. package/src/multistream/multistreamMedia.ts +5 -0
  269. package/src/multistream/receiveSlot.ts +18 -12
  270. package/src/multistream/receiveSlotManager.ts +23 -21
  271. package/src/multistream/remoteMedia.ts +15 -5
  272. package/src/multistream/remoteMediaGroup.ts +4 -3
  273. package/src/multistream/remoteMediaManager.ts +153 -37
  274. package/src/networkQualityMonitor/{index.js → index.ts} +37 -25
  275. package/src/personal-meeting-room/{index.js → index.ts} +28 -19
  276. package/src/personal-meeting-room/{request.js → request.ts} +13 -4
  277. package/src/personal-meeting-room/{util.js → util.ts} +4 -4
  278. package/src/reachability/{index.js → index.ts} +99 -83
  279. package/src/reachability/request.ts +39 -33
  280. package/src/reactions/constants.ts +4 -0
  281. package/src/reactions/reactions.ts +104 -0
  282. package/src/reactions/reactions.type.ts +62 -0
  283. package/src/reconnection-manager/{index.js → index.ts} +195 -102
  284. package/src/recording-controller/enums.ts +8 -0
  285. package/src/recording-controller/index.ts +315 -0
  286. package/src/recording-controller/util.ts +58 -0
  287. package/src/roap/{index.js → index.ts} +73 -56
  288. package/src/roap/request.ts +157 -0
  289. package/src/roap/turnDiscovery.ts +77 -37
  290. package/src/statsAnalyzer/{global.js → global.ts} +30 -33
  291. package/src/statsAnalyzer/{index.js → index.ts} +468 -192
  292. package/src/statsAnalyzer/mqaUtil.ts +290 -0
  293. package/src/transcription/{index.js → index.ts} +46 -39
  294. package/test/integration/spec/journey.js +664 -463
  295. package/test/integration/spec/space-meeting.js +320 -206
  296. package/test/integration/spec/transcription.js +7 -8
  297. package/test/unit/spec/breakouts/breakout.ts +119 -0
  298. package/test/unit/spec/breakouts/collection.ts +15 -0
  299. package/test/unit/spec/breakouts/index.ts +293 -0
  300. package/test/unit/spec/common/browser-detection.js +9 -28
  301. package/test/unit/spec/fixture/locus.js +92 -90
  302. package/test/unit/spec/locus-info/controlsUtils.js +25 -5
  303. package/test/unit/spec/locus-info/embeddedAppsUtils.js +8 -6
  304. package/test/unit/spec/locus-info/index.js +104 -2
  305. package/test/unit/spec/locus-info/infoUtils.js +41 -32
  306. package/test/unit/spec/locus-info/lib/BasicSeqCmp.json +88 -430
  307. package/test/unit/spec/locus-info/lib/SeqCmp.json +513 -685
  308. package/test/unit/spec/locus-info/parser.js +3 -9
  309. package/test/unit/spec/locus-info/selfConstant.js +97 -103
  310. package/test/unit/spec/locus-info/selfUtils.js +105 -12
  311. package/test/unit/spec/media/index.ts +31 -47
  312. package/test/unit/spec/media/properties.ts +9 -9
  313. package/test/unit/spec/meeting/effectsState.js +39 -45
  314. package/test/unit/spec/meeting/in-meeting-actions.ts +5 -2
  315. package/test/unit/spec/meeting/index.js +2017 -742
  316. package/test/unit/spec/meeting/muteState.js +42 -33
  317. package/test/unit/spec/meeting/request.js +115 -44
  318. package/test/unit/spec/meeting/utils.js +104 -171
  319. package/test/unit/spec/meeting-info/meetinginfov2.js +100 -73
  320. package/test/unit/spec/meeting-info/request.js +7 -9
  321. package/test/unit/spec/meeting-info/util.js +11 -12
  322. package/test/unit/spec/meeting-info/utilv2.js +110 -74
  323. package/test/unit/spec/meetings/collection.js +1 -1
  324. package/test/unit/spec/meetings/index.js +439 -257
  325. package/test/unit/spec/meetings/utils.js +14 -12
  326. package/test/unit/spec/member/index.js +0 -1
  327. package/test/unit/spec/member/util.js +31 -7
  328. package/test/unit/spec/members/index.js +104 -54
  329. package/test/unit/spec/members/request.js +29 -20
  330. package/test/unit/spec/members/utils.js +8 -5
  331. package/test/unit/spec/metrics/index.js +16 -21
  332. package/test/unit/spec/multistream/mediaRequestManager.ts +316 -50
  333. package/test/unit/spec/multistream/receiveSlot.ts +6 -6
  334. package/test/unit/spec/multistream/receiveSlotManager.ts +13 -13
  335. package/test/unit/spec/multistream/remoteMedia.ts +10 -2
  336. package/test/unit/spec/multistream/remoteMediaGroup.ts +5 -5
  337. package/test/unit/spec/multistream/remoteMediaManager.ts +412 -65
  338. package/test/unit/spec/networkQualityMonitor/index.js +21 -15
  339. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -7
  340. package/test/unit/spec/reachability/index.ts +58 -26
  341. package/test/unit/spec/reconnection-manager/index.js +102 -9
  342. package/test/unit/spec/recording-controller/index.js +231 -0
  343. package/test/unit/spec/recording-controller/util.js +102 -0
  344. package/test/unit/spec/roap/index.ts +2 -1
  345. package/test/unit/spec/roap/request.ts +114 -0
  346. package/test/unit/spec/roap/turnDiscovery.ts +64 -45
  347. package/test/unit/spec/stats-analyzer/index.js +27 -22
  348. package/test/utils/cmr.js +44 -42
  349. package/test/utils/testUtils.js +83 -74
  350. package/test/utils/webex-config.js +18 -18
  351. package/test/utils/webex-test-users.js +54 -50
  352. package/tsconfig.json +6 -0
  353. package/dist/media/internal-media-core-wrapper.js +0 -22
  354. package/dist/media/internal-media-core-wrapper.js.map +0 -1
  355. package/dist/peer-connection-manager/util.js +0 -124
  356. package/dist/peer-connection-manager/util.js.map +0 -1
  357. package/src/common/logs/logger-proxy.js +0 -33
  358. package/src/locus-info/controlsUtils.js +0 -102
  359. package/src/media/internal-media-core-wrapper.ts +0 -9
  360. package/src/meeting-info/meeting-info-v2.js +0 -255
  361. package/src/meetings/index.js +0 -1015
  362. package/src/peer-connection-manager/util.ts +0 -117
  363. package/src/roap/request.js +0 -127
  364. package/src/statsAnalyzer/mqaUtil.js +0 -173
  365. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -389
@@ -22,7 +22,7 @@ import {
22
22
  LOCUSINFO,
23
23
  PC_BAIL_TIMEOUT,
24
24
  } from '@webex/plugin-meetings/src/constants';
25
- import {MediaConnection as MC} from '@webex/internal-media-core';
25
+ import {ConnectionState, Event, Errors, ErrorType, RemoteTrackType} from '@webex/internal-media-core';
26
26
  import * as StatsAnalyzerModule from '@webex/plugin-meetings/src/statsAnalyzer';
27
27
  import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
28
28
  import Meetings, {CONSTANTS} from '@webex/plugin-meetings';
@@ -36,6 +36,7 @@ import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
36
36
  import Media from '@webex/plugin-meetings/src/media/index';
37
37
  import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
38
38
  import MediaUtil from '@webex/plugin-meetings/src/media/util';
39
+ import RecordingUtil from '@webex/plugin-meetings/src/recording-controller/util';
39
40
  import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
40
41
  import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
41
42
  import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy';
@@ -44,13 +45,18 @@ import Metrics from '@webex/plugin-meetings/src/metrics';
44
45
  import {trigger, eventType} from '@webex/plugin-meetings/src/metrics/config';
45
46
  import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
46
47
  import {IceGatheringFailed} from '@webex/plugin-meetings/src/common/errors/webex-errors';
48
+ import {MediaRequestManager} from '@webex/plugin-meetings/src/multistream/mediaRequestManager';
47
49
 
50
+ import LLM from '@webex/internal-plugin-llm';
51
+ import Mercury from '@webex/internal-plugin-mercury';
52
+ import Breakouts from '@webex/plugin-meetings/src/breakouts';
53
+ import {REACTION_RELAY_TYPES} from '../../../../src/reactions/constants';
48
54
  import locus from '../fixture/locus';
49
55
  import {
50
56
  UserNotJoinedError,
51
57
  MeetingNotActiveError,
52
58
  UserInLobbyError,
53
- NoMediaEstablishedYetError
59
+ NoMediaEstablishedYetError,
54
60
  } from '../../../../src/common/errors/webex-errors';
55
61
  import WebExMeetingsErrors from '../../../../src/common/errors/webex-meetings-error';
56
62
  import ParameterError from '../../../../src/common/errors/parameter';
@@ -59,11 +65,12 @@ import CaptchaError from '../../../../src/common/errors/captcha-error';
59
65
  import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
60
66
  import DefaultSDKConfig from '../../../../src/config';
61
67
  import testUtils from '../../../utils/testUtils';
62
- import {MeetingInfoV2CaptchaError, MeetingInfoV2PasswordError} from '../../../../src/meeting-info/meeting-info-v2';
68
+ import {
69
+ MeetingInfoV2CaptchaError,
70
+ MeetingInfoV2PasswordError,
71
+ } from '../../../../src/meeting-info/meeting-info-v2';
63
72
 
64
- const {
65
- getBrowserName
66
- } = BrowserDetection();
73
+ const {getBrowserName} = BrowserDetection();
67
74
 
68
75
  // Non-stubbed function
69
76
  const {getDisplayMedia} = Media;
@@ -75,7 +82,7 @@ describe('plugin-meetings', () => {
75
82
  error: () => {},
76
83
  warn: () => {},
77
84
  trace: () => {},
78
- debug: () => {}
85
+ debug: () => {},
79
86
  };
80
87
 
81
88
  beforeEach(() => {
@@ -87,44 +94,48 @@ describe('plugin-meetings', () => {
87
94
 
88
95
  before(() => {
89
96
  const MediaStream = {
90
- getVideoTracks: () => [{
91
- applyConstraints: () => { }
92
- }]
97
+ getVideoTracks: () => [
98
+ {
99
+ applyConstraints: () => {},
100
+ },
101
+ ],
93
102
  };
94
103
 
95
104
  Object.defineProperty(global.window.navigator, 'mediaDevices', {
96
105
  writable: true,
97
106
  value: {
98
107
  getDisplayMedia: sinon.stub().returns(Promise.resolve(MediaStream)),
99
- enumerateDevices: sinon.stub().returns(Promise.resolve([
100
- {
101
- deviceId: '',
102
- kind: 'audioinput',
103
- label: '',
104
- groupId: '29d9339cc77bffdd24cb69ee80f6d3200481099bcd0f29267558672de0430777',
105
- },
106
- {
107
- deviceId: '',
108
- kind: 'videoinput',
109
- label: '',
110
- groupId: '08d4f8200e7e4a3425ecf75b7edea9ae4acd934019f2a52217554bcc8e46604d',
111
- },
112
- {
113
- deviceId: '',
114
- kind: 'audiooutput',
115
- label: '',
116
- groupId: '29d9339cc77bffdd24cb69ee80f6d3200481099bcd0f29267558672de0430777',
117
- }
118
- ])),
108
+ enumerateDevices: sinon.stub().returns(
109
+ Promise.resolve([
110
+ {
111
+ deviceId: '',
112
+ kind: 'audioinput',
113
+ label: '',
114
+ groupId: '29d9339cc77bffdd24cb69ee80f6d3200481099bcd0f29267558672de0430777',
115
+ },
116
+ {
117
+ deviceId: '',
118
+ kind: 'videoinput',
119
+ label: '',
120
+ groupId: '08d4f8200e7e4a3425ecf75b7edea9ae4acd934019f2a52217554bcc8e46604d',
121
+ },
122
+ {
123
+ deviceId: '',
124
+ kind: 'audiooutput',
125
+ label: '',
126
+ groupId: '29d9339cc77bffdd24cb69ee80f6d3200481099bcd0f29267558672de0430777',
127
+ },
128
+ ])
129
+ ),
119
130
  getSupportedConstraints: sinon.stub().returns({
120
- sampleRate: true
121
- })
131
+ sampleRate: true,
132
+ }),
122
133
  },
123
134
  });
124
135
 
125
136
  Object.defineProperty(global.window, 'MediaStream', {
126
137
  writable: true,
127
- value: MediaStream
138
+ value: MediaStream,
128
139
  });
129
140
  LoggerConfig.set({verboseEvents: true, enable: false});
130
141
  LoggerProxy.set(logger);
@@ -149,32 +160,34 @@ describe('plugin-meetings', () => {
149
160
  children: {
150
161
  meetings: Meetings,
151
162
  credentials: Credentials,
152
- support: Support
163
+ support: Support,
164
+ llm: LLM,
165
+ mercury: Mercury,
153
166
  },
154
167
  config: {
155
168
  credentials: {
156
- client_id: 'mock-client-id'
169
+ client_id: 'mock-client-id',
157
170
  },
158
171
  meetings: {
159
172
  reconnection: {
160
- enabled: false
173
+ enabled: false,
161
174
  },
162
175
  mediaSettings: {},
163
176
  metrics: {},
164
177
  stats: {},
165
- experimental: {enableUnifiedMeetings: true}
178
+ experimental: {enableUnifiedMeetings: true},
166
179
  },
167
180
  metrics: {
168
- type: ['behavioral']
169
- }
170
- }
181
+ type: ['behavioral'],
182
+ },
183
+ },
171
184
  });
172
185
 
173
186
  webex.internal.support.submitLogs = sinon.stub().returns(Promise.resolve());
174
187
  webex.credentials.getOrgId = sinon.stub().returns('fake-org-id');
175
188
  webex.internal.metrics.submitClientMetrics = sinon.stub().returns(Promise.resolve());
176
189
  webex.meetings.uploadLogs = sinon.stub().returns(Promise.resolve());
177
-
190
+ webex.internal.llm.on = sinon.stub();
178
191
 
179
192
  TriggerProxy.trigger = sinon.stub().returns(true);
180
193
  Metrics.postEvent = sinon.stub();
@@ -203,7 +216,7 @@ describe('plugin-meetings', () => {
203
216
  destinationType: _MEETING_ID_,
204
217
  },
205
218
  {
206
- parent: webex
219
+ parent: webex,
207
220
  }
208
221
  );
209
222
 
@@ -246,6 +259,13 @@ describe('plugin-meetings', () => {
246
259
  assert.equal(meeting.meetingInfoFailureReason, undefined);
247
260
  assert.equal(meeting.destination, testDestination);
248
261
  assert.equal(meeting.destinationType, _MEETING_ID_);
262
+ assert.instanceOf(meeting.breakouts, Breakouts);
263
+ });
264
+ it('creates MediaRequestManager instances', () => {
265
+ assert.instanceOf(meeting.mediaRequestManagers.audio, MediaRequestManager);
266
+ assert.instanceOf(meeting.mediaRequestManagers.video, MediaRequestManager);
267
+ assert.instanceOf(meeting.mediaRequestManagers.screenShareAudio, MediaRequestManager);
268
+ assert.instanceOf(meeting.mediaRequestManagers.screenShareVideo, MediaRequestManager);
249
269
  });
250
270
  });
251
271
  describe('#invite', () => {
@@ -432,7 +452,6 @@ describe('plugin-meetings', () => {
432
452
  it('should return a promise resolution', async () => {
433
453
  meeting.audio = {handleClientRequest};
434
454
 
435
-
436
455
  const audio = meeting.unmuteAudio();
437
456
 
438
457
  assert.exists(audio.then);
@@ -449,8 +468,8 @@ describe('plugin-meetings', () => {
449
468
  readyState: 'live',
450
469
  enabled: true,
451
470
  getSettings: () => ({
452
- sampleRate: 48000
453
- })
471
+ sampleRate: 48000,
472
+ }),
454
473
  });
455
474
 
456
475
  beforeEach(() => {
@@ -458,7 +477,7 @@ describe('plugin-meetings', () => {
458
477
  sinon.replace(meeting, 'addMedia', () => {
459
478
  sinon.stub(meeting.mediaProperties, 'audioTrack').value(fakeMediaTrack());
460
479
  sinon.stub(meeting.mediaProperties, 'mediaDirection').value({
461
- receiveAudio: true
480
+ receiveAudio: true,
462
481
  });
463
482
  });
464
483
  });
@@ -470,7 +489,7 @@ describe('plugin-meetings', () => {
470
489
  describe('before audio attached to meeting', () => {
471
490
  it('should throw no audio error', async () => {
472
491
  await meeting.enableBNR().catch((err) => {
473
- assert.equal(err.toString(), 'Error: Meeting doesn\'t have an audioTrack attached');
492
+ assert.equal(err.toString(), "Error: Meeting doesn't have an audioTrack attached");
474
493
  });
475
494
  });
476
495
  });
@@ -518,7 +537,7 @@ describe('plugin-meetings', () => {
518
537
 
519
538
  it('should throw no audio error', async () => {
520
539
  await meeting.disableBNR().catch((err) => {
521
- assert.equal(err.toString(), 'Error: Meeting doesn\'t have an audioTrack attached');
540
+ assert.equal(err.toString(), "Error: Meeting doesn't have an audioTrack attached");
522
541
  });
523
542
  });
524
543
  });
@@ -650,7 +669,11 @@ describe('plugin-meetings', () => {
650
669
  });
651
670
  describe('#getMediaStreams', () => {
652
671
  beforeEach(() => {
653
- sinon.stub(Media, 'getSupportedDevice').callsFake((options) => Promise.resolve({sendAudio: options.sendAudio, sendVideo: options.sendVideo}));
672
+ sinon
673
+ .stub(Media, 'getSupportedDevice')
674
+ .callsFake((options) =>
675
+ Promise.resolve({sendAudio: options.sendAudio, sendVideo: options.sendVideo})
676
+ );
654
677
  sinon.stub(Media, 'getUserMedia').returns(Promise.resolve(['stream1', 'stream2']));
655
678
  });
656
679
  afterEach(() => {
@@ -674,17 +697,20 @@ describe('plugin-meetings', () => {
674
697
  sinon.stub(meeting.mediaProperties, 'localQualityLevel').value('480p');
675
698
  await meeting.getMediaStreams(mediaDirection, audioVideoSettings);
676
699
 
677
- assert.calledWith(Media.getUserMedia, {
678
- ...mediaDirection,
679
- isSharing: false
680
- },
681
- {
682
- video: {
683
- width: {max: 640, ideal: 640},
684
- height: {max: 480, ideal: 480},
685
- deviceId: videoDevice
700
+ assert.calledWith(
701
+ Media.getUserMedia,
702
+ {
703
+ ...mediaDirection,
704
+ isSharing: false,
705
+ },
706
+ {
707
+ video: {
708
+ width: {max: 640, ideal: 640},
709
+ height: {max: 480, ideal: 480},
710
+ deviceId: videoDevice,
711
+ },
686
712
  }
687
- });
713
+ );
688
714
  });
689
715
  it('will set a new preferred video input device if passed in', async () => {
690
716
  // if audioVideo settings parameter specifies a new video device it
@@ -709,23 +735,33 @@ describe('plugin-meetings', () => {
709
735
  video: {
710
736
  width: {
711
737
  max: 400,
712
- ideal: 400
738
+ ideal: 400,
713
739
  },
714
740
  height: {
715
741
  max: 200,
716
- ideal: 200
717
- }
718
- }
742
+ ideal: 200,
743
+ },
744
+ frameRate: {
745
+ ideal: 15,
746
+ max: 30,
747
+ },
748
+ facingMode: {
749
+ ideal: 'user',
750
+ },
751
+ },
719
752
  };
720
753
 
721
754
  sinon.stub(meeting.mediaProperties, 'localQualityLevel').value('200p');
722
755
  await meeting.getMediaStreams(mediaDirection, customAudioVideoSettings);
723
756
 
724
- assert.calledWith(Media.getUserMedia, {
725
- ...mediaDirection,
726
- isSharing: false
727
- },
728
- customAudioVideoSettings);
757
+ assert.calledWith(
758
+ Media.getUserMedia,
759
+ {
760
+ ...mediaDirection,
761
+ isSharing: false,
762
+ },
763
+ customAudioVideoSettings
764
+ );
729
765
  });
730
766
  it('should not access camera if sendVideo is false ', async () => {
731
767
  await meeting.getMediaStreams({sendAudio: true, sendVideo: false});
@@ -759,13 +795,12 @@ describe('plugin-meetings', () => {
759
795
  });
760
796
  });
761
797
 
762
- it('should throw error', async () => {
798
+ it("should throw error if request doesn't work", async () => {
763
799
  meeting.request = sinon.stub().returns(Promise.reject());
764
800
 
765
801
  try {
766
802
  await meeting.receiveTranscription();
767
- }
768
- catch (err) {
803
+ } catch (err) {
769
804
  assert(err, {});
770
805
  }
771
806
  });
@@ -773,15 +808,74 @@ describe('plugin-meetings', () => {
773
808
  describe('#stopReceivingTranscription', () => {
774
809
  it('should get invoked', () => {
775
810
  meeting.transcription = {
776
- closeSocket: sinon.stub()
811
+ closeSocket: sinon.stub(),
777
812
  };
778
813
 
779
814
  meeting.stopReceivingTranscription();
780
815
  assert.calledOnce(meeting.transcription.closeSocket);
781
816
  });
782
817
  });
818
+ describe('#isReactionsSupported', () => {
819
+ it('should return false if the feature is not supported for the meeting', () => {
820
+ meeting.locusInfo.controls = {reactions: {enabled: false}};
821
+
822
+ assert.equal(meeting.isReactionsSupported(), false);
823
+ });
824
+ it('should return true if the feature is not supported for the meeting', () => {
825
+ meeting.locusInfo.controls = {reactions: {enabled: true}};
826
+
827
+ assert.equal(meeting.isReactionsSupported(), true);
828
+ });
829
+ });
830
+ describe('#processRelayEvent', () => {
831
+ it('should process a Reaction event type', () => {
832
+ meeting.isReactionsSupported = sinon.stub().returns(true);
833
+ meeting.config.receiveReactions = true;
834
+ const fakeSendersName = 'Fake reactors name';
835
+ meeting.members.membersCollection.get = sinon.stub().returns({name: fakeSendersName});
836
+ const fakeReactionPayload = {
837
+ type: 'fake_type',
838
+ codepoints: 'fake_codepoints',
839
+ shortcodes: 'fake_shortcodes',
840
+ tone: {
841
+ type: 'fake_tone_type',
842
+ codepoints: 'fake_tone_codepoints',
843
+ shortcodes: 'fake_tone_shortcodes',
844
+ },
845
+ };
846
+ const fakeSenderPayload = {
847
+ participantId: 'fake_participant_id',
848
+ };
849
+ const fakeProcessedReaction = {
850
+ reaction: fakeReactionPayload,
851
+ sender: {
852
+ id: fakeSenderPayload.participantId,
853
+ name: fakeSendersName,
854
+ },
855
+ };
856
+ const fakeRelayEvent = {
857
+ data: {
858
+ relayType: REACTION_RELAY_TYPES.REACTION,
859
+ reaction: fakeReactionPayload,
860
+ sender: fakeSenderPayload,
861
+ }
862
+ };
863
+ meeting.processRelayEvent(fakeRelayEvent);
864
+ assert.calledWith(
865
+ TriggerProxy.trigger,
866
+ sinon.match.instanceOf(Meeting),
867
+ {
868
+ file: 'meeting/index',
869
+ function: 'join',
870
+ },
871
+ EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
872
+ fakeProcessedReaction
873
+ );
874
+ })
875
+ })
783
876
  describe('#join', () => {
784
877
  let sandbox = null;
878
+ const joinMeetingResult = 'JOIN_MEETINGS_OPTION_RESULT';
785
879
 
786
880
  beforeEach(() => {
787
881
  sandbox = sinon.createSandbox();
@@ -799,22 +893,47 @@ describe('plugin-meetings', () => {
799
893
  meeting.setCorrelationId = sinon.stub().returns(true);
800
894
  meeting.setLocus = sinon.stub().returns(true);
801
895
  webex.meetings.registered = true;
896
+ meeting.updateLLMConnection = sinon.stub();
802
897
  });
803
898
  describe('successful', () => {
804
899
  beforeEach(() => {
805
- sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve());
900
+ sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(joinMeetingResult));
806
901
  });
807
902
 
808
903
  it('should join the meeting and return promise', async () => {
809
904
  const join = meeting.join();
810
905
 
811
- assert.calledWithMatch(Metrics.postEvent, {event: eventType.CALL_INITIATED, data: {trigger: trigger.USER_INTERACTION, isRoapCallEnabled: true}});
906
+ assert.calledWithMatch(Metrics.postEvent, {
907
+ event: eventType.CALL_INITIATED,
908
+ data: {trigger: trigger.USER_INTERACTION, isRoapCallEnabled: true},
909
+ });
812
910
 
813
911
  assert.exists(join.then);
814
- await join;
912
+ const result = await join;
913
+
815
914
  assert.calledOnce(MeetingUtil.joinMeeting);
816
915
  assert.calledOnce(meeting.setLocus);
916
+ assert.equal(result, joinMeetingResult);
917
+ });
918
+
919
+ it('should call updateLLMConnection upon joining if config value is set', async () => {
920
+ meeting.config.enableAutomaticLLM = true;
921
+ meeting.webex.internal.llm.on = sinon.stub();
922
+ meeting.processRelayEvent = sinon.stub();
923
+ await meeting.join();
924
+
925
+ assert.calledOnce(meeting.updateLLMConnection);
926
+ assert.calledOnceWithExactly(meeting.webex.internal.llm.on, 'event:relay.event', meeting.processRelayEvent);
927
+ });
928
+
929
+ it('should not call updateLLMConnection upon joining if config value is not set', async () => {
930
+ meeting.webex.internal.llm.on = sinon.stub();
931
+ await meeting.join();
932
+
933
+ assert.notCalled(meeting.updateLLMConnection);
934
+ assert.notCalled(meeting.webex.internal.llm.on);
817
935
  });
936
+
818
937
  it('should invoke `receiveTranscription()` if receiveTranscription is set to true', async () => {
819
938
  meeting.isTranscriptionSupported = sinon.stub().returns(true);
820
939
  meeting.receiveTranscription = sinon.stub().returns(Promise.resolve());
@@ -856,8 +975,7 @@ describe('plugin-meetings', () => {
856
975
  try {
857
976
  await meeting.join();
858
977
  joinSucceeded = true;
859
- }
860
- catch (e) {
978
+ } catch (e) {
861
979
  assert.instanceOf(e, IntentToJoinError);
862
980
  }
863
981
  assert.isFalse(joinSucceeded);
@@ -905,7 +1023,7 @@ describe('plugin-meetings', () => {
905
1023
  describe('#addMedia', () => {
906
1024
  const muteStateStub = {
907
1025
  handleClientRequest: sinon.stub().returns(Promise.resolve(true)),
908
- applyClientStateLocally: sinon.stub().returns(Promise.resolve(true))
1026
+ applyClientStateLocally: sinon.stub().returns(Promise.resolve(true)),
909
1027
  };
910
1028
 
911
1029
  let fakeMediaConnection;
@@ -913,7 +1031,7 @@ describe('plugin-meetings', () => {
913
1031
  beforeEach(() => {
914
1032
  fakeMediaConnection = {
915
1033
  close: sinon.stub(),
916
- getConnectionState: sinon.stub().returns(MC.ConnectionState.Connected),
1034
+ getConnectionState: sinon.stub().returns(ConnectionState.Connected),
917
1035
  initiateOffer: sinon.stub().resolves({}),
918
1036
  on: sinon.stub(),
919
1037
  };
@@ -926,7 +1044,9 @@ describe('plugin-meetings', () => {
926
1044
  meeting.setMercuryListener = sinon.stub().returns(true);
927
1045
  meeting.setupMediaConnectionListeners = sinon.stub();
928
1046
  meeting.setMercuryListener = sinon.stub();
929
- meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: {}, turnDiscoverySkippedReason: undefined});
1047
+ meeting.roap.doTurnDiscovery = sinon
1048
+ .stub()
1049
+ .resolves({turnServerInfo: {}, turnDiscoverySkippedReason: undefined});
930
1050
  });
931
1051
 
932
1052
  it('should have #addMedia', () => {
@@ -955,8 +1075,7 @@ describe('plugin-meetings', () => {
955
1075
  try {
956
1076
  await meeting.addMedia();
957
1077
  assert.fail('addMedia should have thrown an exception.');
958
- }
959
- catch (err) {
1078
+ } catch (err) {
960
1079
  assert.instanceOf(err, UserInLobbyError);
961
1080
  }
962
1081
  });
@@ -974,37 +1093,33 @@ describe('plugin-meetings', () => {
974
1093
 
975
1094
  assert.isNull(meeting.statsAnalyzer);
976
1095
  assert(Metrics.sendBehavioralMetric.calledOnce);
977
- assert.calledWith(
978
- Metrics.sendBehavioralMetric,
979
- BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
980
- correlation_id: meeting.correlationId,
981
- locus_id: meeting.locusUrl.split('/').pop(),
982
- reason: error.message,
983
- stack: error.stack,
984
- code: error.code,
985
- turnDiscoverySkippedReason: undefined,
986
- turnServerUsed: true
987
- }
988
- );
1096
+ assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
1097
+ correlation_id: meeting.correlationId,
1098
+ locus_id: meeting.locusUrl.split('/').pop(),
1099
+ reason: error.message,
1100
+ stack: error.stack,
1101
+ code: error.code,
1102
+ turnDiscoverySkippedReason: undefined,
1103
+ turnServerUsed: true,
1104
+ });
989
1105
  });
990
1106
 
991
1107
  it('checks metrics called with skipped reason config', async () => {
992
- meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: 'config'});
1108
+ meeting.roap.doTurnDiscovery = sinon
1109
+ .stub()
1110
+ .resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: 'config'});
993
1111
  meeting.meetingState = 'ACTIVE';
994
1112
  await meeting.addMedia().catch((err) => {
995
1113
  assert.exists(err);
996
1114
  assert(Metrics.sendBehavioralMetric.calledOnce);
997
- assert.calledWith(
998
- Metrics.sendBehavioralMetric,
999
- BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
1000
- correlation_id: meeting.correlationId,
1001
- locus_id: meeting.locusUrl.split('/').pop(),
1002
- reason: err.message,
1003
- stack: err.stack,
1004
- turnDiscoverySkippedReason: 'config',
1005
- turnServerUsed: false
1006
- }
1007
- );
1115
+ assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
1116
+ correlation_id: meeting.correlationId,
1117
+ locus_id: meeting.locusUrl.split('/').pop(),
1118
+ reason: err.message,
1119
+ stack: err.stack,
1120
+ turnDiscoverySkippedReason: 'config',
1121
+ turnServerUsed: false,
1122
+ });
1008
1123
  });
1009
1124
  });
1010
1125
 
@@ -1023,12 +1138,13 @@ describe('plugin-meetings', () => {
1023
1138
  assert(Metrics.sendBehavioralMetric.calledOnce);
1024
1139
  assert.calledWith(
1025
1140
  Metrics.sendBehavioralMetric,
1026
- BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, sinon.match({
1141
+ BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
1142
+ sinon.match({
1027
1143
  correlation_id: meeting.correlationId,
1028
1144
  locus_id: meeting.locusUrl.split('/').pop(),
1029
1145
  reason: result.message,
1030
1146
  turnDiscoverySkippedReason: undefined,
1031
- turnServerUsed: true
1147
+ turnServerUsed: true,
1032
1148
  })
1033
1149
  );
1034
1150
  });
@@ -1049,10 +1165,9 @@ describe('plugin-meetings', () => {
1049
1165
 
1050
1166
  try {
1051
1167
  await meeting.addMedia({
1052
- mediaSettings: {}
1168
+ mediaSettings: {},
1053
1169
  });
1054
- }
1055
- catch (err) {
1170
+ } catch (err) {
1056
1171
  assert.fail('should not throw an error');
1057
1172
  }
1058
1173
  });
@@ -1092,11 +1207,13 @@ describe('plugin-meetings', () => {
1092
1207
  });
1093
1208
 
1094
1209
  it('should attach the media and return promise', async () => {
1095
- meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
1210
+ meeting.roap.doTurnDiscovery = sinon
1211
+ .stub()
1212
+ .resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
1096
1213
 
1097
1214
  meeting.meetingState = 'ACTIVE';
1098
1215
  const media = meeting.addMedia({
1099
- mediaSettings: {}
1216
+ mediaSettings: {},
1100
1217
  });
1101
1218
 
1102
1219
  assert.exists(media);
@@ -1105,12 +1222,17 @@ describe('plugin-meetings', () => {
1105
1222
  assert.calledWith(meeting.roap.doTurnDiscovery, meeting, false);
1106
1223
  assert.calledOnce(meeting.mediaProperties.setMediaDirection);
1107
1224
  assert.calledOnce(Media.createMediaConnection);
1108
- assert.calledWith(Media.createMediaConnection, sinon.match.any, sinon.match({turnServerInfo: undefined}));
1225
+ assert.calledWith(
1226
+ Media.createMediaConnection,
1227
+ false,
1228
+ meeting.getMediaConnectionDebugId(),
1229
+ sinon.match({turnServerInfo: undefined})
1230
+ );
1109
1231
  assert.calledOnce(meeting.setMercuryListener);
1110
1232
  assert.calledOnce(fakeMediaConnection.initiateOffer);
1111
1233
  /* statsAnalyzer is initiated inside of addMedia so there isn't
1112
- * a good way to mock it without mocking the constructor
1113
- */
1234
+ * a good way to mock it without mocking the constructor
1235
+ */
1114
1236
  });
1115
1237
 
1116
1238
  it('should pass the turn server info to the peer connection', async () => {
@@ -1121,17 +1243,16 @@ describe('plugin-meetings', () => {
1121
1243
  meeting.meetingState = 'ACTIVE';
1122
1244
  Media.createMediaConnection.resetHistory();
1123
1245
 
1124
-
1125
1246
  meeting.roap.doTurnDiscovery = sinon.stub().resolves({
1126
1247
  turnServerInfo: {
1127
1248
  url: FAKE_TURN_URL,
1128
1249
  username: FAKE_TURN_USER,
1129
- password: FAKE_TURN_PASSWORD
1250
+ password: FAKE_TURN_PASSWORD,
1130
1251
  },
1131
- turnServerSkippedReason: undefined
1252
+ turnServerSkippedReason: undefined,
1132
1253
  });
1133
1254
  const media = meeting.addMedia({
1134
- mediaSettings: {}
1255
+ mediaSettings: {},
1135
1256
  });
1136
1257
 
1137
1258
  assert.exists(media);
@@ -1139,25 +1260,35 @@ describe('plugin-meetings', () => {
1139
1260
  assert.calledOnce(meeting.roap.doTurnDiscovery);
1140
1261
  assert.calledWith(meeting.roap.doTurnDiscovery, meeting, false);
1141
1262
  assert.calledOnce(Media.createMediaConnection);
1142
- assert.calledWith(Media.createMediaConnection, sinon.match.any, sinon.match({
1143
- turnServerInfo: {
1144
- url: FAKE_TURN_URL,
1145
- username: FAKE_TURN_USER,
1146
- password: FAKE_TURN_PASSWORD
1147
- }
1148
- }));
1263
+ assert.calledWith(
1264
+ Media.createMediaConnection,
1265
+ false,
1266
+ meeting.getMediaConnectionDebugId(),
1267
+ sinon.match({
1268
+ turnServerInfo: {
1269
+ url: FAKE_TURN_URL,
1270
+ username: FAKE_TURN_USER,
1271
+ password: FAKE_TURN_PASSWORD,
1272
+ },
1273
+ })
1274
+ );
1149
1275
  assert.calledOnce(fakeMediaConnection.initiateOffer);
1150
1276
  });
1151
1277
 
1152
1278
  it('should attach the media and return WebExMeetingsErrors when connection does not reach CONNECTED state', async () => {
1153
1279
  meeting.meetingState = 'ACTIVE';
1154
- fakeMediaConnection.getConnectionState = sinon.stub().returns(MC.ConnectionState.Connecting);
1280
+ fakeMediaConnection.getConnectionState = sinon
1281
+ .stub()
1282
+ .returns(ConnectionState.Connecting);
1155
1283
  const clock = sinon.useFakeTimers();
1156
1284
  const media = meeting.addMedia({
1157
- mediaSettings: {}
1285
+ mediaSettings: {},
1158
1286
  });
1159
1287
 
1160
- await clock.tickAsync(4000 /* meetingState timer, hardcoded inside addMedia */ + PC_BAIL_TIMEOUT /* connection state timer */);
1288
+ await clock.tickAsync(
1289
+ 4000 /* meetingState timer, hardcoded inside addMedia */ +
1290
+ PC_BAIL_TIMEOUT /* connection state timer */
1291
+ );
1161
1292
  await testUtils.flushPromises();
1162
1293
 
1163
1294
  assert.exists(media);
@@ -1174,9 +1305,10 @@ describe('plugin-meetings', () => {
1174
1305
 
1175
1306
  let errorThrown = false;
1176
1307
 
1177
- await meeting.addMedia({
1178
- mediaSettings: {}
1179
- })
1308
+ await meeting
1309
+ .addMedia({
1310
+ mediaSettings: {},
1311
+ })
1180
1312
  .catch((error) => {
1181
1313
  assert.equal(error.code, IceGatheringFailed.CODE);
1182
1314
  errorThrown = true;
@@ -1188,19 +1320,15 @@ describe('plugin-meetings', () => {
1188
1320
  it('should send ADD_MEDIA_SUCCESS metrics', async () => {
1189
1321
  meeting.meetingState = 'ACTIVE';
1190
1322
  await meeting.addMedia({
1191
- mediaSettings: {}
1323
+ mediaSettings: {},
1192
1324
  });
1193
1325
 
1194
1326
  assert.calledOnce(Metrics.sendBehavioralMetric);
1195
- assert.calledWith(
1196
- Metrics.sendBehavioralMetric,
1197
- BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS,
1198
- {
1199
- correlation_id: meeting.correlationId,
1200
- locus_id: meeting.locusUrl.split('/').pop(),
1201
- connectionType: 'udp'
1202
- }
1203
- );
1327
+ assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS, {
1328
+ correlation_id: meeting.correlationId,
1329
+ locus_id: meeting.locusUrl.split('/').pop(),
1330
+ connectionType: 'udp',
1331
+ });
1204
1332
  });
1205
1333
 
1206
1334
  describe('handles StatsAnalyzer events', () => {
@@ -1218,7 +1346,7 @@ describe('plugin-meetings', () => {
1218
1346
  sinon.stub(StatsAnalyzerModule, 'StatsAnalyzer').returns(statsAnalyzerStub);
1219
1347
 
1220
1348
  await meeting.addMedia({
1221
- mediaSettings: {}
1349
+ mediaSettings: {},
1222
1350
  });
1223
1351
  });
1224
1352
 
@@ -1227,51 +1355,79 @@ describe('plugin-meetings', () => {
1227
1355
  });
1228
1356
 
1229
1357
  it('LOCAL_MEDIA_STARTED triggers "meeting:media:local:start" event and sends metrics', async () => {
1230
- statsAnalyzerStub.emit({file: 'test', function: 'test'}, StatsAnalyzerModule.EVENTS.LOCAL_MEDIA_STARTED, {type: 'audio'});
1358
+ statsAnalyzerStub.emit(
1359
+ {file: 'test', function: 'test'},
1360
+ StatsAnalyzerModule.EVENTS.LOCAL_MEDIA_STARTED,
1361
+ {type: 'audio'}
1362
+ );
1231
1363
 
1232
1364
  assert.calledWith(
1233
1365
  TriggerProxy.trigger,
1234
1366
  sinon.match.instanceOf(Meeting),
1235
1367
  {
1236
1368
  file: 'meeting/index',
1237
- function: 'addMedia'
1369
+ function: 'addMedia',
1238
1370
  },
1239
1371
  EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
1240
1372
  {
1241
- type: 'audio'
1373
+ type: 'audio',
1242
1374
  }
1243
1375
  );
1244
- assert.calledWithMatch(Metrics.postEvent, {event: eventType.SENDING_MEDIA_START, data: {mediaType: 'audio'}});
1376
+ assert.calledWithMatch(Metrics.postEvent, {
1377
+ event: eventType.SENDING_MEDIA_START,
1378
+ data: {mediaType: 'audio'},
1379
+ });
1245
1380
  });
1246
1381
 
1247
1382
  it('LOCAL_MEDIA_STOPPED triggers the right metrics', async () => {
1248
- statsAnalyzerStub.emit({file: 'test', function: 'test'}, StatsAnalyzerModule.EVENTS.LOCAL_MEDIA_STOPPED, {type: 'video'});
1383
+ statsAnalyzerStub.emit(
1384
+ {file: 'test', function: 'test'},
1385
+ StatsAnalyzerModule.EVENTS.LOCAL_MEDIA_STOPPED,
1386
+ {type: 'video'}
1387
+ );
1249
1388
 
1250
- assert.calledWithMatch(Metrics.postEvent, {event: eventType.SENDING_MEDIA_STOP, data: {mediaType: 'video'}});
1389
+ assert.calledWithMatch(Metrics.postEvent, {
1390
+ event: eventType.SENDING_MEDIA_STOP,
1391
+ data: {mediaType: 'video'},
1392
+ });
1251
1393
  });
1252
1394
 
1253
1395
  it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics', async () => {
1254
- statsAnalyzerStub.emit({file: 'test', function: 'test'}, StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STARTED, {type: 'video'});
1396
+ statsAnalyzerStub.emit(
1397
+ {file: 'test', function: 'test'},
1398
+ StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STARTED,
1399
+ {type: 'video'}
1400
+ );
1255
1401
 
1256
1402
  assert.calledWith(
1257
1403
  TriggerProxy.trigger,
1258
1404
  sinon.match.instanceOf(Meeting),
1259
1405
  {
1260
1406
  file: 'meeting/index',
1261
- function: 'addMedia'
1407
+ function: 'addMedia',
1262
1408
  },
1263
1409
  EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
1264
1410
  {
1265
- type: 'video'
1411
+ type: 'video',
1266
1412
  }
1267
1413
  );
1268
- assert.calledWithMatch(Metrics.postEvent, {event: eventType.RECEIVING_MEDIA_START, data: {mediaType: 'video'}});
1414
+ assert.calledWithMatch(Metrics.postEvent, {
1415
+ event: eventType.RECEIVING_MEDIA_START,
1416
+ data: {mediaType: 'video'},
1417
+ });
1269
1418
  });
1270
1419
 
1271
1420
  it('REMOTE_MEDIA_STOPPED triggers the right metrics', async () => {
1272
- statsAnalyzerStub.emit({file: 'test', function: 'test'}, StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STOPPED, {type: 'audio'});
1421
+ statsAnalyzerStub.emit(
1422
+ {file: 'test', function: 'test'},
1423
+ StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STOPPED,
1424
+ {type: 'audio'}
1425
+ );
1273
1426
 
1274
- assert.calledWithMatch(Metrics.postEvent, {event: eventType.RECEIVING_MEDIA_STOP, data: {mediaType: 'audio'}});
1427
+ assert.calledWithMatch(Metrics.postEvent, {
1428
+ event: eventType.RECEIVING_MEDIA_STOP,
1429
+ data: {mediaType: 'audio'},
1430
+ });
1275
1431
  });
1276
1432
 
1277
1433
  it('MEDIA_QUALITY triggers the right metrics', async () => {
@@ -1283,7 +1439,10 @@ describe('plugin-meetings', () => {
1283
1439
  {data: fakeData, networkType: 'wifi'}
1284
1440
  );
1285
1441
 
1286
- assert.calledWithMatch(Metrics.postEvent, {event: eventType.MEDIA_QUALITY, data: {intervalData: fakeData, networkType: 'wifi'}});
1442
+ assert.calledWithMatch(Metrics.postEvent, {
1443
+ event: eventType.MEDIA_QUALITY,
1444
+ data: {intervalData: fakeData, networkType: 'wifi'},
1445
+ });
1287
1446
  });
1288
1447
  });
1289
1448
  });
@@ -1346,7 +1505,9 @@ describe('plugin-meetings', () => {
1346
1505
  sandbox = sinon.createSandbox();
1347
1506
  meeting.meetingFiniteStateMachine.ring();
1348
1507
  meeting.meetingFiniteStateMachine.join();
1349
- meeting.meetingRequest.leaveMeeting = sinon.stub().returns(Promise.resolve({body: 'test'}));
1508
+ meeting.meetingRequest.leaveMeeting = sinon
1509
+ .stub()
1510
+ .returns(Promise.resolve({body: 'test'}));
1350
1511
  meeting.locusInfo.onFullLocus = sinon.stub().returns(true);
1351
1512
  // the 3 need to be promises because we do closeLocalStream.then(closeLocalShare.then) etc in the src code
1352
1513
  meeting.closeLocalStream = sinon.stub().returns(Promise.resolve());
@@ -1361,6 +1522,7 @@ describe('plugin-meetings', () => {
1361
1522
  meeting.unsetRemoteStream = sinon.stub().returns(true);
1362
1523
  meeting.unsetPeerConnections = sinon.stub().returns(true);
1363
1524
  meeting.logger.error = sinon.stub().returns(true);
1525
+ meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
1364
1526
 
1365
1527
  // A meeting needs to be joined to leave
1366
1528
  meeting.meetingState = 'ACTIVE';
@@ -1415,7 +1577,7 @@ describe('plugin-meetings', () => {
1415
1577
  correlationId: meeting.correlationId,
1416
1578
  selfId: meeting.selfId,
1417
1579
  resourceId: null,
1418
- deviceUrl: meeting.deviceUrl
1580
+ deviceUrl: meeting.deviceUrl,
1419
1581
  });
1420
1582
  });
1421
1583
  it('should leave the meeting on the resource', async () => {
@@ -1428,13 +1590,13 @@ describe('plugin-meetings', () => {
1428
1590
  correlationId: meeting.correlationId,
1429
1591
  selfId: meeting.selfId,
1430
1592
  resourceId: meeting.resourceId,
1431
- deviceUrl: meeting.deviceUrl
1593
+ deviceUrl: meeting.deviceUrl,
1432
1594
  });
1433
1595
  });
1434
1596
  });
1435
- describe('#share', () => {
1436
- it('should have #share', () => {
1437
- assert.exists(meeting.share);
1597
+ describe('#requestScreenShareFloor', () => {
1598
+ it('should have #requestScreenShareFloor', () => {
1599
+ assert.exists(meeting.requestScreenShareFloor);
1438
1600
  });
1439
1601
  beforeEach(() => {
1440
1602
  meeting.locusInfo.mediaShares = [{name: 'content', url: url1}];
@@ -1442,7 +1604,7 @@ describe('plugin-meetings', () => {
1442
1604
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
1443
1605
  });
1444
1606
  it('should send the share', async () => {
1445
- const share = meeting.share();
1607
+ const share = meeting.requestScreenShareFloor();
1446
1608
 
1447
1609
  assert.exists(share.then);
1448
1610
  await share;
@@ -1455,7 +1617,9 @@ describe('plugin-meetings', () => {
1455
1617
 
1456
1618
  beforeEach(() => {
1457
1619
  _mediaDirection = meeting.mediaProperties.mediaDirection || {};
1458
- sinon.stub(meeting.mediaProperties, 'mediaDirection').value({sendAudio: true, sendVideo: true, sendShare: false});
1620
+ sinon
1621
+ .stub(meeting.mediaProperties, 'mediaDirection')
1622
+ .value({sendAudio: true, sendVideo: true, sendShare: false});
1459
1623
  });
1460
1624
 
1461
1625
  afterEach(() => {
@@ -1492,7 +1656,11 @@ describe('plugin-meetings', () => {
1492
1656
  it('properly assigns default values', async () => {
1493
1657
  await meeting.shareScreen({sharePreferences: {highFrameRate: true}});
1494
1658
 
1495
- assert.calledWith(Media.getDisplayMedia, {sendShare: true, sendAudio: false, sharePreferences: {highFrameRate: true}});
1659
+ assert.calledWith(Media.getDisplayMedia, {
1660
+ sendShare: true,
1661
+ sendAudio: false,
1662
+ sharePreferences: {highFrameRate: true},
1663
+ });
1496
1664
  });
1497
1665
  });
1498
1666
 
@@ -1522,18 +1690,17 @@ describe('plugin-meetings', () => {
1522
1690
  sendShare,
1523
1691
  receiveShare,
1524
1692
  stream,
1525
- skipSignalingCheck: true
1693
+ skipSignalingCheck: true,
1526
1694
  });
1527
1695
 
1528
1696
  assert.notCalled(meeting.canUpdateMedia);
1529
1697
  });
1530
1698
 
1531
-
1532
1699
  it('skips canUpdateMedia() check on contentTracks.onended', () => {
1533
1700
  const {mediaProperties} = meeting;
1534
1701
  const fakeTrack = {
1535
1702
  getSettings: sinon.stub().returns({}),
1536
- onended: sinon.stub()
1703
+ onended: sinon.stub(),
1537
1704
  };
1538
1705
 
1539
1706
  sandbox.stub(mediaProperties, 'setLocalShareTrack');
@@ -1547,12 +1714,11 @@ describe('plugin-meetings', () => {
1547
1714
  assert.calledWith(meeting.stopShare, {skipSignalingCheck: true});
1548
1715
  });
1549
1716
 
1550
-
1551
1717
  it('stopShare accepts and passes along optional parameters', () => {
1552
1718
  const args = {
1553
1719
  abc: 123,
1554
1720
  receiveShare: false,
1555
- sendShare: false
1721
+ sendShare: false,
1556
1722
  };
1557
1723
 
1558
1724
  sandbox.stub(meeting, 'updateShare').returns(Promise.resolve());
@@ -1589,12 +1755,12 @@ describe('plugin-meetings', () => {
1589
1755
  sinon.match.instanceOf(Meeting),
1590
1756
  {
1591
1757
  file: 'meeting/index',
1592
- function: 'handleShareTrackEnded'
1758
+ function: 'handleShareTrackEnded',
1593
1759
  },
1594
1760
  EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
1595
1761
  {
1596
1762
  stream,
1597
- type: EVENT_TYPES.LOCAL_SHARE
1763
+ type: EVENT_TYPES.LOCAL_SHARE,
1598
1764
  }
1599
1765
  );
1600
1766
  });
@@ -1607,20 +1773,22 @@ describe('plugin-meetings', () => {
1607
1773
  const {resolution} = config;
1608
1774
  const shareOptions = {
1609
1775
  sendShare: true,
1610
- sendAudio: false
1776
+ sendAudio: false,
1611
1777
  };
1612
1778
  const fireFoxOptions = {
1613
1779
  audio: false,
1614
1780
  video: {
1615
1781
  audio: shareOptions.sendAudio,
1616
- video: shareOptions.sendShare
1617
- }
1782
+ video: shareOptions.sendShare,
1783
+ },
1618
1784
  };
1619
1785
 
1620
1786
  const MediaStream = {
1621
- getVideoTracks: () => [{
1622
- applyConstraints: () => {}
1623
- }]
1787
+ getVideoTracks: () => [
1788
+ {
1789
+ applyConstraints: () => {},
1790
+ },
1791
+ ],
1624
1792
  };
1625
1793
 
1626
1794
  const MediaConstraint = {
@@ -1628,7 +1796,7 @@ describe('plugin-meetings', () => {
1628
1796
  aspectRatio: config.aspectRatio,
1629
1797
  frameRate: config.screenFrameRate,
1630
1798
  width: null,
1631
- height: null
1799
+ height: null,
1632
1800
  };
1633
1801
 
1634
1802
  const browserConditionalValue = (value) => {
@@ -1644,31 +1812,23 @@ describe('plugin-meetings', () => {
1644
1812
  if (!global.navigator) {
1645
1813
  global.navigator = {
1646
1814
  mediaDevices: {
1647
- getDisplayMedia: null
1648
- }
1815
+ getDisplayMedia: null,
1816
+ },
1649
1817
  };
1650
1818
  }
1651
1819
  _getDisplayMedia = global.navigator.mediaDevices.getDisplayMedia;
1652
- Object.defineProperty(
1653
- global.navigator.mediaDevices,
1654
- 'getDisplayMedia',
1655
- {
1656
- value: sinon.stub().returns(Promise.resolve(MediaStream)),
1657
- writable: true
1658
- }
1659
- );
1820
+ Object.defineProperty(global.navigator.mediaDevices, 'getDisplayMedia', {
1821
+ value: sinon.stub().returns(Promise.resolve(MediaStream)),
1822
+ writable: true,
1823
+ });
1660
1824
  });
1661
1825
 
1662
1826
  after(() => {
1663
1827
  // clean up for browser
1664
- Object.defineProperty(
1665
- global.navigator.mediaDevices,
1666
- 'getDisplayMedia',
1667
- {
1668
- value: _getDisplayMedia,
1669
- writable: true
1670
- }
1671
- );
1828
+ Object.defineProperty(global.navigator.mediaDevices, 'getDisplayMedia', {
1829
+ value: _getDisplayMedia,
1830
+ writable: true,
1831
+ });
1672
1832
  });
1673
1833
 
1674
1834
  // eslint-disable-next-line max-len
@@ -1680,39 +1840,48 @@ describe('plugin-meetings', () => {
1680
1840
  maxWidth: SHARE_WIDTH,
1681
1841
  maxHeight: SHARE_HEIGHT,
1682
1842
  idealWidth: SHARE_WIDTH,
1683
- idealHeight: SHARE_HEIGHT
1843
+ idealHeight: SHARE_HEIGHT,
1684
1844
  };
1685
1845
 
1686
1846
  // If sharePreferences.shareConstraints is defined it ignores
1687
1847
  // default SDK config settings
1688
- getDisplayMedia({
1689
- ...shareOptions,
1690
- sharePreferences: {shareConstraints}
1691
- }, config);
1848
+ getDisplayMedia(
1849
+ {
1850
+ ...shareOptions,
1851
+ sharePreferences: {shareConstraints},
1852
+ },
1853
+ config
1854
+ );
1692
1855
 
1693
1856
  // eslint-disable-next-line no-undef
1694
- assert.calledWith(navigator.mediaDevices.getDisplayMedia,
1857
+ assert.calledWith(
1858
+ navigator.mediaDevices.getDisplayMedia,
1695
1859
  browserConditionalValue({
1696
1860
  default: {
1697
- video: {...shareConstraints}
1861
+ video: {...shareConstraints},
1698
1862
  },
1699
1863
  // Firefox is being handled differently
1700
- firefox: fireFoxOptions
1701
- }));
1864
+ firefox: fireFoxOptions,
1865
+ })
1866
+ );
1702
1867
  });
1703
1868
 
1704
1869
  // eslint-disable-next-line max-len
1705
1870
  it('will use default resolution if shareConstraints is undefined and highFrameRate is defined', () => {
1706
1871
  // If highFrameRate is defined it ignores default SDK config settings
1707
- getDisplayMedia({
1708
- ...shareOptions,
1709
- sharePreferences: {
1710
- highFrameRate: true
1711
- }
1712
- }, config);
1872
+ getDisplayMedia(
1873
+ {
1874
+ ...shareOptions,
1875
+ sharePreferences: {
1876
+ highFrameRate: true,
1877
+ },
1878
+ },
1879
+ config
1880
+ );
1713
1881
 
1714
1882
  // eslint-disable-next-line no-undef
1715
- assert.calledWith(navigator.mediaDevices.getDisplayMedia,
1883
+ assert.calledWith(
1884
+ navigator.mediaDevices.getDisplayMedia,
1716
1885
  browserConditionalValue({
1717
1886
  default: {
1718
1887
  video: {
@@ -1723,11 +1892,12 @@ describe('plugin-meetings', () => {
1723
1892
  maxWidth: resolution.maxWidth,
1724
1893
  maxHeight: resolution.maxHeight,
1725
1894
  idealWidth: resolution.idealWidth,
1726
- idealHeight: resolution.idealHeight
1727
- }
1895
+ idealHeight: resolution.idealHeight,
1896
+ },
1728
1897
  },
1729
- firefox: fireFoxOptions
1730
- }));
1898
+ firefox: fireFoxOptions,
1899
+ })
1900
+ );
1731
1901
  });
1732
1902
 
1733
1903
  // eslint-disable-next-line max-len
@@ -1736,17 +1906,19 @@ describe('plugin-meetings', () => {
1736
1906
  const {screenResolution} = config;
1737
1907
 
1738
1908
  // eslint-disable-next-line no-undef
1739
- assert.calledWith(navigator.mediaDevices.getDisplayMedia,
1909
+ assert.calledWith(
1910
+ navigator.mediaDevices.getDisplayMedia,
1740
1911
  browserConditionalValue({
1741
1912
  default: {
1742
1913
  video: {
1743
1914
  ...MediaConstraint,
1744
1915
  width: screenResolution.idealWidth,
1745
- height: screenResolution.idealHeight
1746
- }
1916
+ height: screenResolution.idealHeight,
1917
+ },
1747
1918
  },
1748
- firefox: fireFoxOptions
1749
- }));
1919
+ firefox: fireFoxOptions,
1920
+ })
1921
+ );
1750
1922
  });
1751
1923
 
1752
1924
  // Test screenResolution
@@ -1759,14 +1931,15 @@ describe('plugin-meetings', () => {
1759
1931
  maxWidth: SHARE_WIDTH,
1760
1932
  maxHeight: SHARE_HEIGHT,
1761
1933
  idealWidth: SHARE_WIDTH,
1762
- idealHeight: SHARE_HEIGHT
1763
- }
1934
+ idealHeight: SHARE_HEIGHT,
1935
+ },
1764
1936
  };
1765
1937
 
1766
1938
  getDisplayMedia(shareOptions, customConfig);
1767
1939
 
1768
1940
  // eslint-disable-next-line no-undef
1769
- assert.calledWith(navigator.mediaDevices.getDisplayMedia,
1941
+ assert.calledWith(
1942
+ navigator.mediaDevices.getDisplayMedia,
1770
1943
  browserConditionalValue({
1771
1944
  default: {
1772
1945
  video: {
@@ -1776,11 +1949,12 @@ describe('plugin-meetings', () => {
1776
1949
  maxWidth: SHARE_WIDTH,
1777
1950
  maxHeight: SHARE_HEIGHT,
1778
1951
  idealWidth: SHARE_WIDTH,
1779
- idealHeight: SHARE_HEIGHT
1780
- }
1952
+ idealHeight: SHARE_HEIGHT,
1953
+ },
1781
1954
  },
1782
- firefox: fireFoxOptions
1783
- }));
1955
+ firefox: fireFoxOptions,
1956
+ })
1957
+ );
1784
1958
  });
1785
1959
 
1786
1960
  // Test screenFrameRate
@@ -1793,14 +1967,15 @@ describe('plugin-meetings', () => {
1793
1967
  maxWidth: SHARE_WIDTH,
1794
1968
  maxHeight: SHARE_HEIGHT,
1795
1969
  idealWidth: SHARE_WIDTH,
1796
- idealHeight: SHARE_HEIGHT
1797
- }
1970
+ idealHeight: SHARE_HEIGHT,
1971
+ },
1798
1972
  };
1799
1973
 
1800
1974
  getDisplayMedia(shareOptions, customConfig);
1801
1975
 
1802
1976
  // eslint-disable-next-line no-undef
1803
- assert.calledWith(navigator.mediaDevices.getDisplayMedia,
1977
+ assert.calledWith(
1978
+ navigator.mediaDevices.getDisplayMedia,
1804
1979
  browserConditionalValue({
1805
1980
  default: {
1806
1981
  video: {
@@ -1811,11 +1986,12 @@ describe('plugin-meetings', () => {
1811
1986
  maxWidth: SHARE_WIDTH,
1812
1987
  maxHeight: SHARE_HEIGHT,
1813
1988
  idealWidth: SHARE_WIDTH,
1814
- idealHeight: SHARE_HEIGHT
1815
- }
1989
+ idealHeight: SHARE_HEIGHT,
1990
+ },
1816
1991
  },
1817
- firefox: fireFoxOptions
1818
- }));
1992
+ firefox: fireFoxOptions,
1993
+ })
1994
+ );
1819
1995
  });
1820
1996
  });
1821
1997
 
@@ -1857,22 +2033,35 @@ describe('plugin-meetings', () => {
1857
2033
  receiveVideo: true,
1858
2034
  receiveShare: true,
1859
2035
  };
1860
- meeting.mediaProperties.webrtcMediaConnection = {updateSendReceiveOptions: sinon.stub()};
2036
+ meeting.mediaProperties.webrtcMediaConnection = {
2037
+ updateSendReceiveOptions: sinon.stub(),
2038
+ };
1861
2039
  sinon.stub(MeetingUtil, 'getTrack').returns({audioTrack: FAKE_AUDIO_TRACK});
1862
2040
  });
1863
- it('calls this.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions', () => meeting.updateAudio({
1864
- sendAudio: true,
1865
- receiveAudio: true,
1866
- stream: {id: 'fake stream'}
1867
- }).then(() => {
1868
- assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions);
1869
- assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions, {
1870
- send: {audio: FAKE_AUDIO_TRACK},
1871
- receive: {
1872
- audio: true, video: true, screenShareVideo: true, remoteQualityLevel: 'HIGH'
1873
- }
1874
- });
1875
- }));
2041
+ it('calls this.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions', () =>
2042
+ meeting
2043
+ .updateAudio({
2044
+ sendAudio: true,
2045
+ receiveAudio: true,
2046
+ stream: {id: 'fake stream'},
2047
+ })
2048
+ .then(() => {
2049
+ assert.calledOnce(
2050
+ meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions
2051
+ );
2052
+ assert.calledWith(
2053
+ meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions,
2054
+ {
2055
+ send: {audio: FAKE_AUDIO_TRACK},
2056
+ receive: {
2057
+ audio: true,
2058
+ video: true,
2059
+ screenShareVideo: true,
2060
+ remoteQualityLevel: 'HIGH',
2061
+ },
2062
+ }
2063
+ );
2064
+ }));
1876
2065
  });
1877
2066
  afterEach(() => {
1878
2067
  sinon.restore();
@@ -1892,7 +2081,7 @@ describe('plugin-meetings', () => {
1892
2081
 
1893
2082
  meeting.locusInfo.self = {
1894
2083
  enableDTMF: true,
1895
- url: url2
2084
+ url: url2,
1896
2085
  };
1897
2086
 
1898
2087
  await meeting.sendDTMF(tones);
@@ -1900,7 +2089,7 @@ describe('plugin-meetings', () => {
1900
2089
  assert.calledWith(meeting.meetingRequest.sendDTMF, {
1901
2090
  locusUrl: meeting.locusInfo.self.url,
1902
2091
  deviceUrl: meeting.deviceUrl,
1903
- tones
2092
+ tones,
1904
2093
  });
1905
2094
  });
1906
2095
 
@@ -1914,7 +2103,7 @@ describe('plugin-meetings', () => {
1914
2103
  it('should throw an error', () => {
1915
2104
  meeting.locusInfo.self = {
1916
2105
  enableDTMF: false,
1917
- url: url2
2106
+ url: url2,
1918
2107
  };
1919
2108
 
1920
2109
  assert.isRejected(meeting.sendDTMF('123'));
@@ -1939,7 +2128,6 @@ describe('plugin-meetings', () => {
1939
2128
  id: 'fake share track',
1940
2129
  getSettings: sinon.stub().returns({}),
1941
2130
  },
1942
-
1943
2131
  };
1944
2132
 
1945
2133
  beforeEach(() => {
@@ -1971,21 +2159,22 @@ describe('plugin-meetings', () => {
1971
2159
  receiveVideo: true,
1972
2160
  sendShare: true,
1973
2161
  receiveShare: true,
1974
- isSharing: true
2162
+ isSharing: true,
1975
2163
  };
1976
2164
 
1977
2165
  sandbox.stub(meeting, 'canUpdateMedia').returns(false);
1978
2166
  meeting.mediaProperties.webrtcMediaConnection = {
1979
- updateSendReceiveOptions: sinon.stub().resolves({})
2167
+ updateSendReceiveOptions: sinon.stub().resolves({}),
1980
2168
  };
1981
2169
 
1982
2170
  let myPromiseResolved = false;
1983
2171
 
1984
- meeting.updateMedia({
1985
- localStream: mockLocalStream,
1986
- localShare: mockLocalShare,
1987
- mediaSettings
1988
- })
2172
+ meeting
2173
+ .updateMedia({
2174
+ localStream: mockLocalStream,
2175
+ localShare: mockLocalShare,
2176
+ mediaSettings,
2177
+ })
1989
2178
  .then(() => {
1990
2179
  myPromiseResolved = true;
1991
2180
  });
@@ -2002,77 +2191,268 @@ describe('plugin-meetings', () => {
2002
2191
 
2003
2192
  // and check that updateSendReceiveOptions is called with the original args
2004
2193
  assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions);
2005
- assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions, {
2006
- send: {
2007
- audio: FAKE_TRACKS.audio,
2008
- video: FAKE_TRACKS.video,
2009
- screenShareVideo: FAKE_TRACKS.screenshareVideo,
2010
- },
2011
- receive: {
2012
- audio: true,
2013
- video: true,
2014
- screenShareVideo: true,
2015
- remoteQualityLevel: 'HIGH'
2194
+ assert.calledWith(
2195
+ meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions,
2196
+ {
2197
+ send: {
2198
+ audio: FAKE_TRACKS.audio,
2199
+ video: FAKE_TRACKS.video,
2200
+ screenShareVideo: FAKE_TRACKS.screenshareVideo,
2201
+ },
2202
+ receive: {
2203
+ audio: true,
2204
+ video: true,
2205
+ screenShareVideo: true,
2206
+ remoteQualityLevel: 'HIGH',
2207
+ },
2016
2208
  }
2017
- });
2209
+ );
2018
2210
  assert.isTrue(myPromiseResolved);
2019
2211
  });
2020
- });
2021
2212
 
2022
- describe('#changeVideoLayout', () => {
2023
- describe('when media direction has recieve video and there is remoteStream', () => {
2024
- let mediaDirection;
2025
- const layoutTypeSingle = 'Single';
2213
+ it('should request floor only after roap transaction is completed', async () => {
2214
+ const eventListeners = {};
2026
2215
 
2027
- beforeEach(() => {
2028
- mediaDirection = {
2029
- sendAudio: true,
2030
- sendVideo: true,
2031
- sendShare: false,
2032
- receiveVideo: true
2033
- };
2034
- meeting.getMediaStreams = sinon.stub().returns(Promise.resolve([]));
2035
- meeting.updateVideo = sinon.stub().returns(Promise.resolve());
2036
- meeting.mediaProperties.mediaDirection = mediaDirection;
2037
- meeting.mediaProperties.remoteVideoTrack = sinon.stub().returns({mockTrack: 'mockTrack'});
2216
+ meeting.webex.meetings.reachability = {
2217
+ isAnyClusterReachable: sandbox.stub().resolves(true)
2218
+ };
2038
2219
 
2039
- meeting.meetingRequest.changeVideoLayoutDebounced = sinon.stub().returns(Promise.resolve());
2220
+ const fakeMediaConnection = {
2221
+ close: sinon.stub(),
2222
+ getConnectionState: sinon.stub().returns(ConnectionState.Connected),
2223
+ initiateOffer: sinon.stub().resolves({}),
2040
2224
 
2041
- meeting.locusInfo.self = {
2042
- url: url2
2043
- };
2044
- });
2225
+ // mock the on() method and store all the listeners
2226
+ on: sinon.stub().callsFake((event, listener) => {
2227
+ eventListeners[event] = listener;
2228
+ }),
2229
+
2230
+ updateSendReceiveOptions: sinon.stub().callsFake(() => {
2231
+ // trigger ROAP_STARTED before updateSendReceiveOptions() resolves
2232
+ if (eventListeners[Event.ROAP_STARTED]) {
2233
+ eventListeners[Event.ROAP_STARTED]();
2234
+ } else {
2235
+ throw new Error('ROAP_STARTED listener not registered')
2236
+ }
2237
+ return Promise.resolve();
2238
+ }),
2239
+ };
2045
2240
 
2046
- it('should listen once for CONTROLS_MEETING_LAYOUT_UPDATED', async () => {
2047
- // const spy = sinon.spy(TriggerProxy, 'trigger');
2048
- const spy = sinon.spy(meeting.locusInfo, 'once');
2241
+ meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
2242
+ meeting.mediaProperties.getCurrentConnectionType = sinon.stub().resolves('udp');
2243
+ Media.createMediaConnection = sinon.stub().returns(fakeMediaConnection);
2049
2244
 
2050
- await meeting.changeVideoLayout('Equal');
2245
+ const requestScreenShareFloorStub = sandbox.stub(meeting, 'requestScreenShareFloor').resolves({});
2051
2246
 
2052
- assert.calledWith(spy, LOCUSINFO.EVENTS.CONTROLS_MEETING_LAYOUT_UPDATED);
2053
- });
2247
+ let myPromiseResolved = false;
2054
2248
 
2055
- it('should have receiveVideo true and remote video track should exist', () => {
2056
- assert.equal(meeting.mediaProperties.mediaDirection.receiveVideo, true);
2057
- assert.exists(meeting.mediaProperties.remoteVideoTrack);
2249
+ meeting.meetingState = 'ACTIVE';
2250
+ await meeting.addMedia({
2251
+ mediaSettings: {},
2058
2252
  });
2059
2253
 
2060
- it('has layoutType which exists in the list of allowed layoutTypes and should call meetingRequest changeVideoLayoutDebounced method', async () => {
2061
- const layoutType = 'Equal';
2254
+ meeting
2255
+ .updateMedia({
2256
+ localShare: mockLocalShare,
2257
+ mediaSettings: {
2258
+ sendShare: true,
2259
+ },
2260
+ })
2261
+ .then(() => {
2262
+ myPromiseResolved = true;
2263
+ });
2062
2264
 
2063
- await meeting.changeVideoLayout(layoutType);
2265
+ await testUtils.flushPromises();
2064
2266
 
2065
- assert(CONSTANTS.LAYOUT_TYPES.includes(layoutType));
2066
- assert.calledWith(meeting.meetingRequest.changeVideoLayoutDebounced, {
2067
- locusUrl: meeting.locusInfo.self.url,
2267
+ assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions);
2268
+ assert.isFalse(myPromiseResolved);
2269
+
2270
+ // verify that requestScreenShareFloorStub was not called yet
2271
+ assert.notCalled(requestScreenShareFloorStub);
2272
+
2273
+ eventListeners[Event.ROAP_DONE]();
2274
+ await testUtils.flushPromises();
2275
+
2276
+ // now it should have been called
2277
+ assert.calledOnce(requestScreenShareFloorStub);
2278
+ });
2279
+ });
2280
+
2281
+ describe('#updateShare', () => {
2282
+ const mockLocalShare = {id: 'mock local share stream'};
2283
+ let eventListeners;
2284
+ let fakeMediaConnection;
2285
+ let requestScreenShareFloorStub;
2286
+
2287
+ const FAKE_TRACKS = {
2288
+ screenshareVideo: {
2289
+ id: 'fake share track',
2290
+ getSettings: sinon.stub().returns({}),
2291
+ },
2292
+ };
2293
+
2294
+ beforeEach(async () => {
2295
+ eventListeners = {};
2296
+
2297
+ sinon.stub(MeetingUtil, 'getTrack').callsFake((stream) => {
2298
+ if (stream === mockLocalShare) {
2299
+ return {audioTrack: null, videoTrack: FAKE_TRACKS.screenshareVideo};
2300
+ }
2301
+
2302
+ return {audioTrack: null, videoTrack: null};
2303
+ });
2304
+
2305
+ meeting.webex.meetings.reachability = {
2306
+ isAnyClusterReachable: sinon.stub().resolves(true)
2307
+ };
2308
+
2309
+ fakeMediaConnection = {
2310
+ close: sinon.stub(),
2311
+ getConnectionState: sinon.stub().returns(ConnectionState.Connected),
2312
+ initiateOffer: sinon.stub().resolves({}),
2313
+
2314
+ // mock the on() method and store all the listeners
2315
+ on: sinon.stub().callsFake((event, listener) => {
2316
+ eventListeners[event] = listener;
2317
+ }),
2318
+
2319
+ updateSendReceiveOptions: sinon.stub().callsFake(() => {
2320
+ // trigger ROAP_STARTED before updateSendReceiveOptions() resolves
2321
+ if (eventListeners[Event.ROAP_STARTED]) {
2322
+ eventListeners[Event.ROAP_STARTED]();
2323
+ } else {
2324
+ throw new Error('ROAP_STARTED listener not registered')
2325
+ }
2326
+ return Promise.resolve();
2327
+ }),
2328
+ };
2329
+
2330
+ meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
2331
+ meeting.mediaProperties.getCurrentConnectionType = sinon.stub().resolves('udp');
2332
+ Media.createMediaConnection = sinon.stub().returns(fakeMediaConnection);
2333
+
2334
+ requestScreenShareFloorStub = sinon.stub(meeting, 'requestScreenShareFloor').resolves({});
2335
+
2336
+ meeting.meetingState = 'ACTIVE';
2337
+ await meeting.addMedia({
2338
+ mediaSettings: {},
2339
+ });
2340
+ });
2341
+
2342
+ afterEach(() => {
2343
+ sinon.restore();
2344
+ });
2345
+
2346
+ it('when starting share, it should request floor only after roap transaction is completed', async () => {
2347
+ let myPromiseResolved = false;
2348
+
2349
+ meeting
2350
+ .updateShare({
2351
+ sendShare: true,
2352
+ receiveShare: true,
2353
+ stream: mockLocalShare,
2354
+ })
2355
+ .then(() => {
2356
+ myPromiseResolved = true;
2357
+ });
2358
+
2359
+ await testUtils.flushPromises();
2360
+
2361
+ assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions);
2362
+ assert.isFalse(myPromiseResolved);
2363
+
2364
+ // verify that requestScreenShareFloorStub was not called yet
2365
+ assert.notCalled(requestScreenShareFloorStub);
2366
+
2367
+ eventListeners[Event.ROAP_DONE]();
2368
+ await testUtils.flushPromises();
2369
+
2370
+ // now it should have been called
2371
+ assert.calledOnce(requestScreenShareFloorStub);
2372
+ });
2373
+
2374
+ it('when changing screen share stream and no roap transaction happening, it requests floor immediately', async () => {
2375
+ let myPromiseResolved = false;
2376
+
2377
+ // simulate a case when no roap transaction is triggered by updateSendReceiveOptions
2378
+ meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions = sinon.stub().resolves({});
2379
+
2380
+ meeting
2381
+ .updateShare({
2382
+ sendShare: true,
2383
+ receiveShare: true,
2384
+ stream: mockLocalShare,
2385
+ })
2386
+ .then(() => {
2387
+ myPromiseResolved = true;
2388
+ });
2389
+
2390
+ await testUtils.flushPromises();
2391
+
2392
+ assert.calledOnce(meeting.mediaProperties.webrtcMediaConnection.updateSendReceiveOptions);
2393
+ assert.calledOnce(requestScreenShareFloorStub);
2394
+ assert.isTrue(myPromiseResolved);
2395
+ });
2396
+ });
2397
+
2398
+ describe('#changeVideoLayout', () => {
2399
+ describe('when media direction has recieve video and there is remoteStream', () => {
2400
+ let mediaDirection;
2401
+ const layoutTypeSingle = 'Single';
2402
+
2403
+ beforeEach(() => {
2404
+ mediaDirection = {
2405
+ sendAudio: true,
2406
+ sendVideo: true,
2407
+ sendShare: false,
2408
+ receiveVideo: true,
2409
+ };
2410
+ meeting.getMediaStreams = sinon.stub().returns(Promise.resolve([]));
2411
+ meeting.updateVideo = sinon.stub().returns(Promise.resolve());
2412
+ meeting.mediaProperties.mediaDirection = mediaDirection;
2413
+ meeting.mediaProperties.remoteVideoTrack = sinon
2414
+ .stub()
2415
+ .returns({mockTrack: 'mockTrack'});
2416
+
2417
+ meeting.meetingRequest.changeVideoLayoutDebounced = sinon
2418
+ .stub()
2419
+ .returns(Promise.resolve());
2420
+
2421
+ meeting.locusInfo.self = {
2422
+ url: url2,
2423
+ };
2424
+ });
2425
+
2426
+ it('should listen once for CONTROLS_MEETING_LAYOUT_UPDATED', async () => {
2427
+ // const spy = sinon.spy(TriggerProxy, 'trigger');
2428
+ const spy = sinon.spy(meeting.locusInfo, 'once');
2429
+
2430
+ await meeting.changeVideoLayout('Equal');
2431
+
2432
+ assert.calledWith(spy, LOCUSINFO.EVENTS.CONTROLS_MEETING_LAYOUT_UPDATED);
2433
+ });
2434
+
2435
+ it('should have receiveVideo true and remote video track should exist', () => {
2436
+ assert.equal(meeting.mediaProperties.mediaDirection.receiveVideo, true);
2437
+ assert.exists(meeting.mediaProperties.remoteVideoTrack);
2438
+ });
2439
+
2440
+ it('has layoutType which exists in the list of allowed layoutTypes and should call meetingRequest changeVideoLayoutDebounced method', async () => {
2441
+ const layoutType = 'Equal';
2442
+
2443
+ await meeting.changeVideoLayout(layoutType);
2444
+
2445
+ assert(CONSTANTS.LAYOUT_TYPES.includes(layoutType));
2446
+ assert.calledWith(meeting.meetingRequest.changeVideoLayoutDebounced, {
2447
+ locusUrl: meeting.locusInfo.self.url,
2068
2448
  deviceUrl: meeting.deviceUrl,
2069
2449
  layoutType,
2070
2450
  main: undefined,
2071
- content: undefined
2451
+ content: undefined,
2072
2452
  });
2073
2453
  });
2074
2454
 
2075
- it('doesn\'t have layoutType which exists in the list of allowed layoutTypes should throw an error', async () => {
2455
+ it("doesn't have layoutType which exists in the list of allowed layoutTypes should throw an error", async () => {
2076
2456
  const layoutType = 'Invalid Layout';
2077
2457
 
2078
2458
  assert.isRejected(meeting.changeVideoLayout(layoutType));
@@ -2086,12 +2466,14 @@ describe('plugin-meetings', () => {
2086
2466
  deviceUrl: meeting.deviceUrl,
2087
2467
  layoutType: undefined,
2088
2468
  main: {width: 100, height: 200},
2089
- content: undefined
2469
+ content: undefined,
2090
2470
  });
2091
2471
  });
2092
2472
 
2093
2473
  it('throws if trying to send renderInfo for content when not receiving content', async () => {
2094
- assert.isRejected(meeting.changeVideoLayout(layoutTypeSingle, {content: {width: 1280, height: 720}}));
2474
+ assert.isRejected(
2475
+ meeting.changeVideoLayout(layoutTypeSingle, {content: {width: 1280, height: 720}})
2476
+ );
2095
2477
  });
2096
2478
 
2097
2479
  it('calls changeVideoLayoutDebounced with renderInfo for main and content', async () => {
@@ -2103,7 +2485,7 @@ describe('plugin-meetings', () => {
2103
2485
  deviceUrl: meeting.deviceUrl,
2104
2486
  layoutType: layoutTypeSingle,
2105
2487
  main: {width: 100, height: 200},
2106
- content: undefined
2488
+ content: undefined,
2107
2489
  });
2108
2490
 
2109
2491
  meeting.mediaProperties.mediaDirection.receiveShare = true;
@@ -2117,18 +2499,21 @@ describe('plugin-meetings', () => {
2117
2499
  deviceUrl: meeting.deviceUrl,
2118
2500
  layoutType: layoutTypeSingle,
2119
2501
  main: {width: 100, height: 200},
2120
- content: {width: 500, height: 600}
2502
+ content: {width: 500, height: 600},
2121
2503
  });
2122
2504
 
2123
2505
  // and now call with both
2124
- await meeting.changeVideoLayout(layoutTypeSingle, {main: {width: 300, height: 400}, content: {width: 700, height: 800}});
2506
+ await meeting.changeVideoLayout(layoutTypeSingle, {
2507
+ main: {width: 300, height: 400},
2508
+ content: {width: 700, height: 800},
2509
+ });
2125
2510
 
2126
2511
  assert.calledWith(meeting.meetingRequest.changeVideoLayoutDebounced, {
2127
2512
  locusUrl: meeting.locusInfo.self.url,
2128
2513
  deviceUrl: meeting.deviceUrl,
2129
2514
  layoutType: layoutTypeSingle,
2130
2515
  main: {width: 300, height: 400},
2131
- content: {width: 700, height: 800}
2516
+ content: {width: 700, height: 800},
2132
2517
  });
2133
2518
 
2134
2519
  // and now set just the layoutType, the previous main and content values should be used
@@ -2141,7 +2526,7 @@ describe('plugin-meetings', () => {
2141
2526
  deviceUrl: meeting.deviceUrl,
2142
2527
  layoutType,
2143
2528
  main: {width: 300, height: 400},
2144
- content: {width: 700, height: 800}
2529
+ content: {width: 700, height: 800},
2145
2530
  });
2146
2531
  });
2147
2532
 
@@ -2153,7 +2538,7 @@ describe('plugin-meetings', () => {
2153
2538
  deviceUrl: meeting.deviceUrl,
2154
2539
  layoutType: layoutTypeSingle,
2155
2540
  main: {width: 1024, height: 768},
2156
- content: undefined
2541
+ content: undefined,
2157
2542
  });
2158
2543
  meeting.meetingRequest.changeVideoLayoutDebounced.resetHistory();
2159
2544
 
@@ -2175,28 +2560,39 @@ describe('plugin-meetings', () => {
2175
2560
  meeting.mediaProperties.mediaDirection.receiveShare = true;
2176
2561
  meeting.mediaProperties.remoteShare = sinon.stub().returns({mockTrack: 'mockTrack'});
2177
2562
 
2178
- await meeting.changeVideoLayout(layoutTypeSingle, {main: {width: 500, height: 510}, content: {width: 1024, height: 768}});
2563
+ await meeting.changeVideoLayout(layoutTypeSingle, {
2564
+ main: {width: 500, height: 510},
2565
+ content: {width: 1024, height: 768},
2566
+ });
2179
2567
 
2180
2568
  assert.calledWith(meeting.meetingRequest.changeVideoLayoutDebounced, {
2181
2569
  locusUrl: meeting.locusInfo.self.url,
2182
2570
  deviceUrl: meeting.deviceUrl,
2183
2571
  layoutType: layoutTypeSingle,
2184
2572
  main: {width: 500, height: 510},
2185
- content: {width: 1024, height: 768}
2573
+ content: {width: 1024, height: 768},
2186
2574
  });
2187
2575
  meeting.meetingRequest.changeVideoLayoutDebounced.resetHistory();
2188
2576
 
2189
2577
  // now send main with width/height different by just 2px - it should be ignored
2190
- await meeting.changeVideoLayout(layoutTypeSingle, {content: {width: 1026, height: 768}});
2578
+ await meeting.changeVideoLayout(layoutTypeSingle, {
2579
+ content: {width: 1026, height: 768},
2580
+ });
2191
2581
  assert.notCalled(meeting.meetingRequest.changeVideoLayoutDebounced);
2192
2582
 
2193
- await meeting.changeVideoLayout(layoutTypeSingle, {content: {width: 1022, height: 768}});
2583
+ await meeting.changeVideoLayout(layoutTypeSingle, {
2584
+ content: {width: 1022, height: 768},
2585
+ });
2194
2586
  assert.notCalled(meeting.meetingRequest.changeVideoLayoutDebounced);
2195
2587
 
2196
- await meeting.changeVideoLayout(layoutTypeSingle, {content: {width: 1024, height: 770}});
2588
+ await meeting.changeVideoLayout(layoutTypeSingle, {
2589
+ content: {width: 1024, height: 770},
2590
+ });
2197
2591
  assert.notCalled(meeting.meetingRequest.changeVideoLayoutDebounced);
2198
2592
 
2199
- await meeting.changeVideoLayout(layoutTypeSingle, {content: {width: 1024, height: 766}});
2593
+ await meeting.changeVideoLayout(layoutTypeSingle, {
2594
+ content: {width: 1024, height: 766},
2595
+ });
2200
2596
  assert.notCalled(meeting.meetingRequest.changeVideoLayoutDebounced);
2201
2597
  });
2202
2598
 
@@ -2204,14 +2600,17 @@ describe('plugin-meetings', () => {
2204
2600
  meeting.mediaProperties.mediaDirection.receiveShare = true;
2205
2601
  meeting.mediaProperties.remoteShare = sinon.stub().returns({mockTrack: 'mockTrack'});
2206
2602
 
2207
- await meeting.changeVideoLayout(layoutTypeSingle, {main: {width: 500.5, height: 510.09}, content: {width: 1024.2, height: 768.85}});
2603
+ await meeting.changeVideoLayout(layoutTypeSingle, {
2604
+ main: {width: 500.5, height: 510.09},
2605
+ content: {width: 1024.2, height: 768.85},
2606
+ });
2208
2607
 
2209
2608
  assert.calledWith(meeting.meetingRequest.changeVideoLayoutDebounced, {
2210
2609
  locusUrl: meeting.locusInfo.self.url,
2211
2610
  deviceUrl: meeting.deviceUrl,
2212
2611
  layoutType: layoutTypeSingle,
2213
2612
  main: {width: 501, height: 510},
2214
- content: {width: 1024, height: 769}
2613
+ content: {width: 1024, height: 769},
2215
2614
  });
2216
2615
  });
2217
2616
  });
@@ -2223,7 +2622,7 @@ describe('plugin-meetings', () => {
2223
2622
  sendAudio: true,
2224
2623
  sendVideo: true,
2225
2624
  sendShare: false,
2226
- receiveVideo: true
2625
+ receiveVideo: true,
2227
2626
  };
2228
2627
 
2229
2628
  meeting.mediaProperties.mediaDirection = mediaDirection;
@@ -2237,7 +2636,7 @@ describe('plugin-meetings', () => {
2237
2636
  sendAudio: true,
2238
2637
  sendVideo: true,
2239
2638
  sendShare: false,
2240
- receiveVideo: false
2639
+ receiveVideo: false,
2241
2640
  };
2242
2641
 
2243
2642
  meeting.mediaProperties.mediaDirection = mediaDirection;
@@ -2251,8 +2650,8 @@ describe('plugin-meetings', () => {
2251
2650
 
2252
2651
  const fakeTrack = {getSettings: () => ({height: 720})};
2253
2652
  const USER_AGENT_CHROME_MAC =
2254
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
2255
- 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36';
2653
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
2654
+ 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36';
2256
2655
 
2257
2656
  beforeEach(() => {
2258
2657
  mediaDirection = {sendAudio: true, sendVideo: true, sendShare: false};
@@ -2268,34 +2667,36 @@ describe('plugin-meetings', () => {
2268
2667
  assert.exists(meeting.setLocalVideoQuality);
2269
2668
  });
2270
2669
 
2271
- it('should call getMediaStreams with the proper level', () => meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
2272
- delete mediaDirection.receiveVideo;
2273
- assert.calledWith(meeting.getMediaStreams,
2274
- mediaDirection,
2275
- CONSTANTS.VIDEO_RESOLUTIONS[CONSTANTS.QUALITY_LEVELS.LOW]);
2276
- }));
2670
+ it('should call getMediaStreams with the proper level', () =>
2671
+ meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
2672
+ delete mediaDirection.receiveVideo;
2673
+ assert.calledWith(
2674
+ meeting.getMediaStreams,
2675
+ mediaDirection,
2676
+ CONSTANTS.VIDEO_RESOLUTIONS[CONSTANTS.QUALITY_LEVELS.LOW]
2677
+ );
2678
+ }));
2277
2679
 
2278
2680
  it('when browser is chrome then it should stop previous video track', () => {
2279
2681
  meeting.mediaProperties.videoTrack = fakeTrack;
2280
- assert.equal(
2281
- BrowserDetection(USER_AGENT_CHROME_MAC).getBrowserName(),
2282
- 'Chrome'
2283
- );
2284
- meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS.LOW)
2285
- .then(() => {
2286
- assert.calledWith(Media.stopTracks, fakeTrack);
2287
- });
2682
+ assert.equal(BrowserDetection(USER_AGENT_CHROME_MAC).getBrowserName(), 'Chrome');
2683
+ meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
2684
+ assert.calledWith(Media.stopTracks, fakeTrack);
2685
+ });
2288
2686
  });
2289
2687
 
2290
- it('should set mediaProperty with the proper level', () => meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
2291
- assert.equal(meeting.mediaProperties.localQualityLevel, CONSTANTS.QUALITY_LEVELS.LOW);
2292
- }));
2688
+ it('should set mediaProperty with the proper level', () =>
2689
+ meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
2690
+ assert.equal(meeting.mediaProperties.localQualityLevel, CONSTANTS.QUALITY_LEVELS.LOW);
2691
+ }));
2293
2692
 
2294
2693
  it('when device does not support 1080p then it should set localQualityLevel with highest possible resolution', () => {
2295
- meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS['1080p'])
2296
- .then(() => {
2297
- assert.equal(meeting.mediaProperties.localQualityLevel, CONSTANTS.QUALITY_LEVELS['720p']);
2298
- });
2694
+ meeting.setLocalVideoQuality(CONSTANTS.QUALITY_LEVELS['1080p']).then(() => {
2695
+ assert.equal(
2696
+ meeting.mediaProperties.localQualityLevel,
2697
+ CONSTANTS.QUALITY_LEVELS['720p']
2698
+ );
2699
+ });
2299
2700
  });
2300
2701
 
2301
2702
  it('should error if set to a invalid level', () => {
@@ -2321,13 +2722,15 @@ describe('plugin-meetings', () => {
2321
2722
  assert.exists(meeting.setRemoteQualityLevel);
2322
2723
  });
2323
2724
 
2324
- it('should set mediaProperty with the proper level', () => meeting.setRemoteQualityLevel(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
2325
- assert.equal(meeting.mediaProperties.remoteQualityLevel, CONSTANTS.QUALITY_LEVELS.LOW);
2326
- }));
2725
+ it('should set mediaProperty with the proper level', () =>
2726
+ meeting.setRemoteQualityLevel(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
2727
+ assert.equal(meeting.mediaProperties.remoteQualityLevel, CONSTANTS.QUALITY_LEVELS.LOW);
2728
+ }));
2327
2729
 
2328
- it('should call updateMedia', () => meeting.setRemoteQualityLevel(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
2329
- assert.calledOnce(meeting.updateMedia);
2330
- }));
2730
+ it('should call updateMedia', () =>
2731
+ meeting.setRemoteQualityLevel(CONSTANTS.QUALITY_LEVELS.LOW).then(() => {
2732
+ assert.calledOnce(meeting.updateMedia);
2733
+ }));
2331
2734
 
2332
2735
  it('should error if set to a invalid level', () => {
2333
2736
  assert.isRejected(meeting.setRemoteQualityLevel('invalid'));
@@ -2341,8 +2744,12 @@ describe('plugin-meetings', () => {
2341
2744
 
2342
2745
  describe('#usePhoneAudio', () => {
2343
2746
  beforeEach(() => {
2344
- meeting.meetingRequest.dialIn = sinon.stub().returns(Promise.resolve({body: {locus: 'testData'}}));
2345
- meeting.meetingRequest.dialOut = sinon.stub().returns(Promise.resolve({body: {locus: 'testData'}}));
2747
+ meeting.meetingRequest.dialIn = sinon
2748
+ .stub()
2749
+ .returns(Promise.resolve({body: {locus: 'testData'}}));
2750
+ meeting.meetingRequest.dialOut = sinon
2751
+ .stub()
2752
+ .returns(Promise.resolve({body: {locus: 'testData'}}));
2346
2753
  meeting.locusInfo.onFullLocus = sinon.stub().returns(Promise.resolve());
2347
2754
  });
2348
2755
 
@@ -2354,7 +2761,7 @@ describe('plugin-meetings', () => {
2354
2761
  correlationId: meeting.correlationId,
2355
2762
  dialInUrl: DIAL_IN_URL,
2356
2763
  locusUrl: meeting.locusUrl,
2357
- clientUrl: meeting.deviceUrl
2764
+ clientUrl: meeting.deviceUrl,
2358
2765
  });
2359
2766
  assert.calledWith(meeting.locusInfo.onFullLocus, 'testData');
2360
2767
  assert.notCalled(meeting.meetingRequest.dialOut);
@@ -2369,7 +2776,7 @@ describe('plugin-meetings', () => {
2369
2776
  correlationId: meeting.correlationId,
2370
2777
  dialInUrl: DIAL_IN_URL,
2371
2778
  locusUrl: meeting.locusUrl,
2372
- clientUrl: meeting.deviceUrl
2779
+ clientUrl: meeting.deviceUrl,
2373
2780
  });
2374
2781
  assert.calledWith(meeting.locusInfo.onFullLocus, 'testData');
2375
2782
  assert.notCalled(meeting.meetingRequest.dialOut);
@@ -2386,7 +2793,7 @@ describe('plugin-meetings', () => {
2386
2793
  dialOutUrl: DIAL_OUT_URL,
2387
2794
  locusUrl: meeting.locusUrl,
2388
2795
  clientUrl: meeting.deviceUrl,
2389
- phoneNumber
2796
+ phoneNumber,
2390
2797
  });
2391
2798
  assert.calledWith(meeting.locusInfo.onFullLocus, 'testData');
2392
2799
  assert.notCalled(meeting.meetingRequest.dialIn);
@@ -2402,7 +2809,7 @@ describe('plugin-meetings', () => {
2402
2809
  dialOutUrl: DIAL_OUT_URL,
2403
2810
  locusUrl: meeting.locusUrl,
2404
2811
  clientUrl: meeting.deviceUrl,
2405
- phoneNumber
2812
+ phoneNumber,
2406
2813
  });
2407
2814
  assert.calledWith(meeting.locusInfo.onFullLocus, 'testData');
2408
2815
  assert.notCalled(meeting.meetingRequest.dialIn);
@@ -2413,11 +2820,14 @@ describe('plugin-meetings', () => {
2413
2820
 
2414
2821
  meeting.meetingRequest.dialIn = sinon.stub().returns(Promise.reject(error));
2415
2822
 
2416
- return meeting.usePhoneAudio().then(() => Promise.reject(new Error('Promise resolved when it should have rejected'))).catch((e) => {
2417
- assert.equal(e, error);
2823
+ return meeting
2824
+ .usePhoneAudio()
2825
+ .then(() => Promise.reject(new Error('Promise resolved when it should have rejected')))
2826
+ .catch((e) => {
2827
+ assert.equal(e, error);
2418
2828
 
2419
- return Promise.resolve();
2420
- });
2829
+ return Promise.resolve();
2830
+ });
2421
2831
  });
2422
2832
 
2423
2833
  it('rejects if the request failed (dial out)', async () => {
@@ -2425,11 +2835,14 @@ describe('plugin-meetings', () => {
2425
2835
 
2426
2836
  meeting.meetingRequest.dialOut = sinon.stub().returns(Promise.reject(error));
2427
2837
 
2428
- return meeting.usePhoneAudio('+441234567890').then(() => Promise.reject(new Error('Promise resolved when it should have rejected'))).catch((e) => {
2429
- assert.equal(e, error);
2838
+ return meeting
2839
+ .usePhoneAudio('+441234567890')
2840
+ .then(() => Promise.reject(new Error('Promise resolved when it should have rejected')))
2841
+ .catch((e) => {
2842
+ assert.equal(e, error);
2430
2843
 
2431
- return Promise.resolve();
2432
- });
2844
+ return Promise.resolve();
2845
+ });
2433
2846
  });
2434
2847
  });
2435
2848
 
@@ -2448,34 +2861,42 @@ describe('plugin-meetings', () => {
2448
2861
  locusUrl: 'some_locus_url',
2449
2862
  sipUrl: 'some_sip_url', // or sipMeetingUri
2450
2863
  meetingNumber: '123456', // this.config.experimental.enableUnifiedMeetings
2451
- hostId: 'some_host_id' // this.owner;
2864
+ hostId: 'some_host_id', // this.owner;
2452
2865
  };
2453
2866
  const FAKE_SDK_CAPTCHA_INFO = {
2454
2867
  captchaId: FAKE_CAPTCHA_ID,
2455
2868
  verificationImageURL: FAKE_CAPTCHA_IMAGE_URL,
2456
2869
  verificationAudioURL: FAKE_CAPTCHA_AUDIO_URL,
2457
- refreshURL: FAKE_CAPTCHA_REFRESH_URL
2870
+ refreshURL: FAKE_CAPTCHA_REFRESH_URL,
2458
2871
  };
2459
2872
  const FAKE_WBXAPPAPI_CAPTCHA_INFO = {
2460
2873
  captchaID: `${FAKE_CAPTCHA_ID}-2`,
2461
2874
  verificationImageURL: `${FAKE_CAPTCHA_IMAGE_URL}-2`,
2462
2875
  verificationAudioURL: `${FAKE_CAPTCHA_AUDIO_URL}-2`,
2463
- refreshURL: `${FAKE_CAPTCHA_REFRESH_URL}-2`
2876
+ refreshURL: `${FAKE_CAPTCHA_REFRESH_URL}-2`,
2464
2877
  };
2465
2878
 
2466
-
2467
2879
  it('calls meetingInfoProvider with all the right parameters and parses the result', async () => {
2468
- meeting.attrs.meetingInfoProvider = {fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO})};
2880
+ meeting.attrs.meetingInfoProvider = {
2881
+ fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO}),
2882
+ };
2469
2883
  meeting.requiredCaptcha = FAKE_SDK_CAPTCHA_INFO;
2470
2884
  meeting.destination = FAKE_DESTINATION;
2471
2885
  meeting.destinationType = FAKE_TYPE;
2472
2886
  meeting.parseMeetingInfo = sinon.stub().returns(undefined);
2473
2887
 
2474
2888
  await meeting.fetchMeetingInfo({
2475
- password: FAKE_PASSWORD, captchaCode: FAKE_CAPTCHA_CODE
2889
+ password: FAKE_PASSWORD,
2890
+ captchaCode: FAKE_CAPTCHA_CODE,
2476
2891
  });
2477
2892
 
2478
- assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, FAKE_PASSWORD, {code: FAKE_CAPTCHA_CODE, id: FAKE_CAPTCHA_ID});
2893
+ assert.calledWith(
2894
+ meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
2895
+ FAKE_DESTINATION,
2896
+ FAKE_TYPE,
2897
+ FAKE_PASSWORD,
2898
+ {code: FAKE_CAPTCHA_CODE, id: FAKE_CAPTCHA_ID}
2899
+ );
2479
2900
 
2480
2901
  assert.calledWith(meeting.parseMeetingInfo, {body: FAKE_MEETING_INFO}, FAKE_DESTINATION);
2481
2902
  assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
@@ -2483,11 +2904,18 @@ describe('plugin-meetings', () => {
2483
2904
  assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.NONE);
2484
2905
  assert.equal(meeting.requiredCaptcha, null);
2485
2906
  assert.calledTwice(TriggerProxy.trigger);
2486
- assert.calledWith(TriggerProxy.trigger, meeting, {file: 'meetings', function: 'fetchMeetingInfo'}, 'meeting:meetingInfoAvailable');
2907
+ assert.calledWith(
2908
+ TriggerProxy.trigger,
2909
+ meeting,
2910
+ {file: 'meetings', function: 'fetchMeetingInfo'},
2911
+ 'meeting:meetingInfoAvailable'
2912
+ );
2487
2913
  });
2488
2914
 
2489
2915
  it('calls meetingInfoProvider with all the right parameters and parses the result when random delay is applied', async () => {
2490
- meeting.attrs.meetingInfoProvider = {fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO})};
2916
+ meeting.attrs.meetingInfoProvider = {
2917
+ fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO}),
2918
+ };
2491
2919
  meeting.destination = FAKE_DESTINATION;
2492
2920
  meeting.destinationType = FAKE_TYPE;
2493
2921
  meeting.parseMeetingInfo = sinon.stub().returns(undefined);
@@ -2504,7 +2932,13 @@ describe('plugin-meetings', () => {
2504
2932
  assert.isUndefined(meeting.fetchMeetingInfoTimeoutId);
2505
2933
 
2506
2934
  // meeting info provider
2507
- assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, null, null);
2935
+ assert.calledWith(
2936
+ meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
2937
+ FAKE_DESTINATION,
2938
+ FAKE_TYPE,
2939
+ null,
2940
+ null
2941
+ );
2508
2942
 
2509
2943
  // parseMeeting info
2510
2944
  assert.calledWith(meeting.parseMeetingInfo, {body: FAKE_MEETING_INFO}, FAKE_DESTINATION);
@@ -2515,31 +2949,48 @@ describe('plugin-meetings', () => {
2515
2949
  assert.equal(meeting.passwordStatus, PASSWORD_STATUS.NOT_REQUIRED);
2516
2950
 
2517
2951
  assert.calledTwice(TriggerProxy.trigger);
2518
- assert.calledWith(TriggerProxy.trigger, meeting, {file: 'meetings', function: 'fetchMeetingInfo'}, 'meeting:meetingInfoAvailable');
2952
+ assert.calledWith(
2953
+ TriggerProxy.trigger,
2954
+ meeting,
2955
+ {file: 'meetings', function: 'fetchMeetingInfo'},
2956
+ 'meeting:meetingInfoAvailable'
2957
+ );
2519
2958
  });
2520
2959
 
2521
2960
  it('fails if captchaCode is provided when captcha not needed', async () => {
2522
- meeting.attrs.meetingInfoProvider = {fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO})};
2961
+ meeting.attrs.meetingInfoProvider = {
2962
+ fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO}),
2963
+ };
2523
2964
  meeting.requiredCaptcha = null;
2524
2965
  meeting.destination = FAKE_DESTINATION;
2525
2966
  meeting.destinationType = FAKE_TYPE;
2526
2967
 
2527
- await assert.isRejected(meeting.fetchMeetingInfo({
2528
- captchaCode: FAKE_CAPTCHA_CODE
2529
- }), Error, 'fetchMeetingInfo() called with captchaCode when captcha was not required');
2968
+ await assert.isRejected(
2969
+ meeting.fetchMeetingInfo({
2970
+ captchaCode: FAKE_CAPTCHA_CODE,
2971
+ }),
2972
+ Error,
2973
+ 'fetchMeetingInfo() called with captchaCode when captcha was not required'
2974
+ );
2530
2975
 
2531
2976
  assert.notCalled(meeting.attrs.meetingInfoProvider.fetchMeetingInfo);
2532
2977
  });
2533
2978
 
2534
2979
  it('fails if password is provided when not required', async () => {
2535
- meeting.attrs.meetingInfoProvider = {fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO})};
2980
+ meeting.attrs.meetingInfoProvider = {
2981
+ fetchMeetingInfo: sinon.stub().resolves({body: FAKE_MEETING_INFO}),
2982
+ };
2536
2983
  meeting.passwordStatus = PASSWORD_STATUS.NOT_REQUIRED;
2537
2984
  meeting.destination = FAKE_DESTINATION;
2538
2985
  meeting.destinationType = FAKE_TYPE;
2539
2986
 
2540
- await assert.isRejected(meeting.fetchMeetingInfo({
2541
- password: FAKE_PASSWORD
2542
- }), Error, 'fetchMeetingInfo() called with password when password was not required');
2987
+ await assert.isRejected(
2988
+ meeting.fetchMeetingInfo({
2989
+ password: FAKE_PASSWORD,
2990
+ }),
2991
+ Error,
2992
+ 'fetchMeetingInfo() called with password when password was not required'
2993
+ );
2543
2994
 
2544
2995
  assert.notCalled(meeting.attrs.meetingInfoProvider.fetchMeetingInfo);
2545
2996
  });
@@ -2548,15 +2999,26 @@ describe('plugin-meetings', () => {
2548
2999
  meeting.destination = FAKE_DESTINATION;
2549
3000
  meeting.destinationType = FAKE_TYPE;
2550
3001
  meeting.attrs.meetingInfoProvider = {
2551
- fetchMeetingInfo: sinon.stub().throws(new MeetingInfoV2PasswordError(403004, FAKE_MEETING_INFO))
3002
+ fetchMeetingInfo: sinon
3003
+ .stub()
3004
+ .throws(new MeetingInfoV2PasswordError(403004, FAKE_MEETING_INFO)),
2552
3005
  };
2553
3006
 
2554
3007
  await assert.isRejected(meeting.fetchMeetingInfo({}), PasswordError);
2555
3008
 
2556
- assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, null, null);
3009
+ assert.calledWith(
3010
+ meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
3011
+ FAKE_DESTINATION,
3012
+ FAKE_TYPE,
3013
+ null,
3014
+ null
3015
+ );
2557
3016
 
2558
3017
  assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
2559
- assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD);
3018
+ assert.equal(
3019
+ meeting.meetingInfoFailureReason,
3020
+ MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD
3021
+ );
2560
3022
  assert.equal(meeting.requiredCaptcha, null);
2561
3023
  assert.equal(meeting.passwordStatus, PASSWORD_STATUS.REQUIRED);
2562
3024
  });
@@ -2565,25 +3027,38 @@ describe('plugin-meetings', () => {
2565
3027
  meeting.destination = FAKE_DESTINATION;
2566
3028
  meeting.destinationType = FAKE_TYPE;
2567
3029
  meeting.attrs.meetingInfoProvider = {
2568
- fetchMeetingInfo: sinon.stub().throws(new MeetingInfoV2CaptchaError(423005, FAKE_SDK_CAPTCHA_INFO))
3030
+ fetchMeetingInfo: sinon
3031
+ .stub()
3032
+ .throws(new MeetingInfoV2CaptchaError(423005, FAKE_SDK_CAPTCHA_INFO)),
2569
3033
  };
2570
3034
  meeting.requiredCaptcha = null;
2571
3035
 
2572
- await assert.isRejected(meeting.fetchMeetingInfo({
2573
- password: 'aaa'
2574
- }), CaptchaError);
2575
-
2576
- assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, 'aaa', null);
3036
+ await assert.isRejected(
3037
+ meeting.fetchMeetingInfo({
3038
+ password: 'aaa',
3039
+ }),
3040
+ CaptchaError
3041
+ );
2577
3042
 
3043
+ assert.calledWith(
3044
+ meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
3045
+ FAKE_DESTINATION,
3046
+ FAKE_TYPE,
3047
+ 'aaa',
3048
+ null
3049
+ );
2578
3050
 
2579
3051
  assert.deepEqual(meeting.meetingInfo, {});
2580
- assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD);
3052
+ assert.equal(
3053
+ meeting.meetingInfoFailureReason,
3054
+ MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD
3055
+ );
2581
3056
  assert.equal(meeting.passwordStatus, PASSWORD_STATUS.REQUIRED);
2582
3057
  assert.deepEqual(meeting.requiredCaptcha, {
2583
3058
  captchaId: FAKE_CAPTCHA_ID,
2584
3059
  verificationImageURL: FAKE_CAPTCHA_IMAGE_URL,
2585
3060
  verificationAudioURL: FAKE_CAPTCHA_AUDIO_URL,
2586
- refreshURL: FAKE_CAPTCHA_REFRESH_URL
3061
+ refreshURL: FAKE_CAPTCHA_REFRESH_URL,
2587
3062
  });
2588
3063
  });
2589
3064
 
@@ -2591,15 +3066,27 @@ describe('plugin-meetings', () => {
2591
3066
  meeting.destination = FAKE_DESTINATION;
2592
3067
  meeting.destinationType = FAKE_TYPE;
2593
3068
  meeting.attrs.meetingInfoProvider = {
2594
- fetchMeetingInfo: sinon.stub().throws(new MeetingInfoV2CaptchaError(423005, FAKE_SDK_CAPTCHA_INFO))
3069
+ fetchMeetingInfo: sinon
3070
+ .stub()
3071
+ .throws(new MeetingInfoV2CaptchaError(423005, FAKE_SDK_CAPTCHA_INFO)),
2595
3072
  };
2596
3073
  meeting.requiredCaptcha = FAKE_SDK_CAPTCHA_INFO;
2597
3074
 
2598
- await assert.isRejected(meeting.fetchMeetingInfo({
2599
- password: 'aaa', captchaCode: 'bbb'
2600
- }), CaptchaError);
3075
+ await assert.isRejected(
3076
+ meeting.fetchMeetingInfo({
3077
+ password: 'aaa',
3078
+ captchaCode: 'bbb',
3079
+ }),
3080
+ CaptchaError
3081
+ );
2601
3082
 
2602
- assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, 'aaa', {code: 'bbb', id: FAKE_CAPTCHA_ID});
3083
+ assert.calledWith(
3084
+ meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
3085
+ FAKE_DESTINATION,
3086
+ FAKE_TYPE,
3087
+ 'aaa',
3088
+ {code: 'bbb', id: FAKE_CAPTCHA_ID}
3089
+ );
2603
3090
 
2604
3091
  assert.deepEqual(meeting.meetingInfo, {});
2605
3092
  assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.WRONG_CAPTCHA);
@@ -2611,20 +3098,24 @@ describe('plugin-meetings', () => {
2611
3098
  meeting.destination = FAKE_DESTINATION;
2612
3099
  meeting.destinationType = FAKE_TYPE;
2613
3100
  meeting.attrs.meetingInfoProvider = {
2614
- fetchMeetingInfo: sinon.stub().resolves(
2615
- {
2616
- statusCode: 200,
2617
- body: FAKE_MEETING_INFO
2618
- }
2619
- )
3101
+ fetchMeetingInfo: sinon.stub().resolves({
3102
+ statusCode: 200,
3103
+ body: FAKE_MEETING_INFO,
3104
+ }),
2620
3105
  };
2621
3106
  meeting.passwordStatus = PASSWORD_STATUS.REQUIRED;
2622
3107
 
2623
3108
  await meeting.fetchMeetingInfo({
2624
- password: 'aaa'
3109
+ password: 'aaa',
2625
3110
  });
2626
3111
 
2627
- assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, 'aaa', null);
3112
+ assert.calledWith(
3113
+ meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
3114
+ FAKE_DESTINATION,
3115
+ FAKE_TYPE,
3116
+ 'aaa',
3117
+ null
3118
+ );
2628
3119
 
2629
3120
  assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
2630
3121
  assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.NONE);
@@ -2638,36 +3129,50 @@ describe('plugin-meetings', () => {
2638
3129
  const refreshedCaptcha = {
2639
3130
  captchaID: FAKE_WBXAPPAPI_CAPTCHA_INFO.captchaID,
2640
3131
  verificationImageURL: FAKE_WBXAPPAPI_CAPTCHA_INFO.verificationImageURL,
2641
- verificationAudioURL: FAKE_WBXAPPAPI_CAPTCHA_INFO.verificationAudioURL
3132
+ verificationAudioURL: FAKE_WBXAPPAPI_CAPTCHA_INFO.verificationAudioURL,
2642
3133
  };
2643
3134
 
2644
3135
  meeting.attrs.meetingInfoProvider = {
2645
- fetchMeetingInfo: sinon.stub().throws(new MeetingInfoV2PasswordError(403004, FAKE_MEETING_INFO))
3136
+ fetchMeetingInfo: sinon
3137
+ .stub()
3138
+ .throws(new MeetingInfoV2PasswordError(403004, FAKE_MEETING_INFO)),
2646
3139
  };
2647
- meeting.meetingRequest.refreshCaptcha = sinon.stub().returns(Promise.resolve(
2648
- {
2649
- body: refreshedCaptcha
2650
- }
2651
- ));
3140
+ meeting.meetingRequest.refreshCaptcha = sinon.stub().returns(
3141
+ Promise.resolve({
3142
+ body: refreshedCaptcha,
3143
+ })
3144
+ );
2652
3145
  meeting.passwordStatus = PASSWORD_STATUS.REQUIRED;
2653
3146
  meeting.requiredCaptcha = FAKE_SDK_CAPTCHA_INFO;
2654
3147
  meeting.destination = FAKE_DESTINATION;
2655
3148
  meeting.destinationType = FAKE_TYPE;
2656
3149
 
2657
- await assert.isRejected(meeting.fetchMeetingInfo({
2658
- password: 'aaa', captchaCode: 'bbb'
2659
- }));
3150
+ await assert.isRejected(
3151
+ meeting.fetchMeetingInfo({
3152
+ password: 'aaa',
3153
+ captchaCode: 'bbb',
3154
+ })
3155
+ );
2660
3156
 
2661
- assert.calledWith(meeting.attrs.meetingInfoProvider.fetchMeetingInfo, FAKE_DESTINATION, FAKE_TYPE, 'aaa', {code: 'bbb', id: FAKE_CAPTCHA_ID});
3157
+ assert.calledWith(
3158
+ meeting.attrs.meetingInfoProvider.fetchMeetingInfo,
3159
+ FAKE_DESTINATION,
3160
+ FAKE_TYPE,
3161
+ 'aaa',
3162
+ {code: 'bbb', id: FAKE_CAPTCHA_ID}
3163
+ );
2662
3164
 
2663
3165
  assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
2664
- assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD);
3166
+ assert.equal(
3167
+ meeting.meetingInfoFailureReason,
3168
+ MEETING_INFO_FAILURE_REASON.WRONG_PASSWORD
3169
+ );
2665
3170
  assert.equal(meeting.passwordStatus, PASSWORD_STATUS.REQUIRED);
2666
3171
  assert.deepEqual(meeting.requiredCaptcha, {
2667
3172
  captchaId: refreshedCaptcha.captchaID,
2668
3173
  verificationImageURL: refreshedCaptcha.verificationImageURL,
2669
3174
  verificationAudioURL: refreshedCaptcha.verificationAudioURL,
2670
- refreshURL: FAKE_SDK_CAPTCHA_INFO.refreshURL // refresh url doesn't change
3175
+ refreshURL: FAKE_SDK_CAPTCHA_INFO.refreshURL, // refresh url doesn't change
2671
3176
  });
2672
3177
  });
2673
3178
  });
@@ -2677,48 +3182,56 @@ describe('plugin-meetings', () => {
2677
3182
  assert.isRejected(meeting.refreshCaptcha(), Error);
2678
3183
  });
2679
3184
  it('sends correct request to captcha service refresh url', async () => {
2680
- const REFRESH_URL = 'https://something.webex.com/captchaservice/v1/captchas/refresh?blablabla=something&captchaID=xxx';
2681
- const EXPECTED_REFRESH_URL = 'https://something.webex.com/captchaservice/v1/captchas/refresh?blablabla=something&captchaID=xxx&siteFullName=something.webex.com';
3185
+ const REFRESH_URL =
3186
+ 'https://something.webex.com/captchaservice/v1/captchas/refresh?blablabla=something&captchaID=xxx';
3187
+ const EXPECTED_REFRESH_URL =
3188
+ 'https://something.webex.com/captchaservice/v1/captchas/refresh?blablabla=something&captchaID=xxx&siteFullName=something.webex.com';
2682
3189
 
2683
3190
  const FAKE_SDK_CAPTCHA_INFO = {
2684
3191
  captchaId: 'some id',
2685
3192
  verificationImageURL: 'some image url',
2686
3193
  verificationAudioURL: 'some audio url',
2687
- refreshURL: REFRESH_URL
3194
+ refreshURL: REFRESH_URL,
2688
3195
  };
2689
3196
 
2690
3197
  const FAKE_REFRESHED_CAPTCHA = {
2691
3198
  captchaID: 'some id',
2692
3199
  verificationImageURL: 'some image url',
2693
- verificationAudioURL: 'some audio url'
3200
+ verificationAudioURL: 'some audio url',
2694
3201
  };
2695
3202
 
2696
3203
  // setup the meeting so that a captcha is required
2697
3204
  meeting.attrs.meetingInfoProvider = {
2698
- fetchMeetingInfo: sinon.stub().throws(new MeetingInfoV2CaptchaError(423005, FAKE_SDK_CAPTCHA_INFO))
3205
+ fetchMeetingInfo: sinon
3206
+ .stub()
3207
+ .throws(new MeetingInfoV2CaptchaError(423005, FAKE_SDK_CAPTCHA_INFO)),
2699
3208
  };
2700
3209
 
2701
- await assert.isRejected(meeting.fetchMeetingInfo({
2702
- password: ''
2703
- }), CaptchaError);
3210
+ await assert.isRejected(
3211
+ meeting.fetchMeetingInfo({
3212
+ password: '',
3213
+ }),
3214
+ CaptchaError
3215
+ );
2704
3216
 
2705
3217
  assert.deepEqual(meeting.requiredCaptcha, FAKE_SDK_CAPTCHA_INFO);
2706
- meeting.meetingRequest.refreshCaptcha = sinon.stub().returns(Promise.resolve({body: FAKE_REFRESHED_CAPTCHA}));
3218
+ meeting.meetingRequest.refreshCaptcha = sinon
3219
+ .stub()
3220
+ .returns(Promise.resolve({body: FAKE_REFRESHED_CAPTCHA}));
2707
3221
 
2708
3222
  // test the captcha refresh
2709
3223
  await meeting.refreshCaptcha();
2710
3224
 
2711
- assert.calledWith(meeting.meetingRequest.refreshCaptcha,
2712
- {
2713
- captchaRefreshUrl: EXPECTED_REFRESH_URL,
2714
- captchaId: FAKE_SDK_CAPTCHA_INFO.captchaId
2715
- });
3225
+ assert.calledWith(meeting.meetingRequest.refreshCaptcha, {
3226
+ captchaRefreshUrl: EXPECTED_REFRESH_URL,
3227
+ captchaId: FAKE_SDK_CAPTCHA_INFO.captchaId,
3228
+ });
2716
3229
 
2717
3230
  assert.deepEqual(meeting.requiredCaptcha, {
2718
3231
  captchaId: FAKE_REFRESHED_CAPTCHA.captchaID,
2719
3232
  verificationImageURL: FAKE_REFRESHED_CAPTCHA.verificationImageURL,
2720
3233
  verificationAudioURL: FAKE_REFRESHED_CAPTCHA.verificationAudioURL,
2721
- refreshURL: FAKE_SDK_CAPTCHA_INFO.refreshURL // refresh url doesn't change
3234
+ refreshURL: FAKE_SDK_CAPTCHA_INFO.refreshURL, // refresh url doesn't change
2722
3235
  });
2723
3236
  });
2724
3237
  });
@@ -2732,7 +3245,7 @@ describe('plugin-meetings', () => {
2732
3245
  assert(Metrics.sendBehavioralMetric.calledOnce);
2733
3246
  assert.calledWith(
2734
3247
  Metrics.sendBehavioralMetric,
2735
- BEHAVIORAL_METRICS.VERIFY_PASSWORD_SUCCESS,
3248
+ BEHAVIORAL_METRICS.VERIFY_PASSWORD_SUCCESS
2736
3249
  );
2737
3250
  assert.equal(result.isPasswordValid, true);
2738
3251
  assert.equal(result.requiredCaptcha, null);
@@ -2807,7 +3320,9 @@ describe('plugin-meetings', () => {
2807
3320
  sandbox = sinon.createSandbox();
2808
3321
  meeting.meetingFiniteStateMachine.ring();
2809
3322
  meeting.meetingFiniteStateMachine.join();
2810
- meeting.meetingRequest.endMeetingForAll = sinon.stub().returns(Promise.resolve({body: 'test'}));
3323
+ meeting.meetingRequest.endMeetingForAll = sinon
3324
+ .stub()
3325
+ .returns(Promise.resolve({body: 'test'}));
2811
3326
  meeting.locusInfo.onFullLocus = sinon.stub().returns(true);
2812
3327
  meeting.closeLocalStream = sinon.stub().returns(Promise.resolve());
2813
3328
  meeting.closeLocalShare = sinon.stub().returns(Promise.resolve());
@@ -2821,6 +3336,7 @@ describe('plugin-meetings', () => {
2821
3336
  meeting.unsetRemoteStream = sinon.stub().returns(true);
2822
3337
  meeting.unsetPeerConnections = sinon.stub().returns(true);
2823
3338
  meeting.logger.error = sinon.stub().returns(true);
3339
+ meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
2824
3340
 
2825
3341
  // A meeting needs to be joined to end
2826
3342
  meeting.meetingState = 'ACTIVE';
@@ -2859,7 +3375,11 @@ describe('plugin-meetings', () => {
2859
3375
  sandbox.stub(meeting.mediaProperties, 'unsetMediaTracks');
2860
3376
 
2861
3377
  sandbox.stub(meeting.reconnectionManager, 'reconnectMedia').returns(Promise.resolve());
2862
- sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(MeetingUtil.parseLocusJoin({body: {locus, mediaConnections: []}})));
3378
+ sandbox
3379
+ .stub(MeetingUtil, 'joinMeeting')
3380
+ .returns(
3381
+ Promise.resolve(MeetingUtil.parseLocusJoin({body: {locus, mediaConnections: []}}))
3382
+ );
2863
3383
  });
2864
3384
 
2865
3385
  afterEach(() => {
@@ -2870,8 +3390,7 @@ describe('plugin-meetings', () => {
2870
3390
  it('should throw an error if resourceId not passed', async () => {
2871
3391
  try {
2872
3392
  await meeting.moveTo();
2873
- }
2874
- catch (err) {
3393
+ } catch (err) {
2875
3394
  assert.instanceOf(err, ParameterError);
2876
3395
  assert.equal(err.sdkMessage, 'Cannot move call without a resourceId.');
2877
3396
  }
@@ -2888,17 +3407,17 @@ describe('plugin-meetings', () => {
2888
3407
  share: true,
2889
3408
  share_audio: false,
2890
3409
  video: false,
2891
- whiteboard: false
3410
+ whiteboard: false,
2892
3411
  },
2893
3412
  tx: {
2894
3413
  audio: false,
2895
3414
  share: false,
2896
3415
  share_audio: false,
2897
3416
  video: false,
2898
- whiteboard: false
2899
- }
2900
- }
2901
- }
3417
+ whiteboard: false,
3418
+ },
3419
+ },
3420
+ },
2902
3421
  });
2903
3422
  assert.calledWithMatch(Metrics.postEvent, {event: eventType.MOVE_MEDIA});
2904
3423
  });
@@ -2907,17 +3426,19 @@ describe('plugin-meetings', () => {
2907
3426
  sinon.spy(MeetingUtil, 'joinMeetingOptions');
2908
3427
  await meeting.moveTo('resourceId');
2909
3428
 
2910
- assert.calledWith(MeetingUtil.joinMeetingOptions, meeting, {resourceId: 'resourceId', moveToResource: true});
3429
+ assert.calledWith(MeetingUtil.joinMeetingOptions, meeting, {
3430
+ resourceId: 'resourceId',
3431
+ moveToResource: true,
3432
+ });
2911
3433
  });
2912
3434
 
2913
3435
  it('should reconnectMedia after DX joins after moveTo', async () => {
2914
3436
  await meeting.moveTo('resourceId');
2915
3437
 
2916
-
2917
3438
  await meeting.locusInfo.emitScoped(
2918
3439
  {
2919
3440
  file: 'locus-info',
2920
- function: 'updateSelf'
3441
+ function: 'updateSelf',
2921
3442
  },
2922
3443
  'SELF_OBSERVING'
2923
3444
  );
@@ -2933,36 +3454,30 @@ describe('plugin-meetings', () => {
2933
3454
  assert.called(meeting.mediaProperties.setMediaDirection);
2934
3455
  assert.called(meeting.mediaProperties.unsetMediaTracks);
2935
3456
 
2936
- assert.calledWith(meeting.reconnectionManager.reconnectMedia,
2937
- {
2938
- mediaDirection: {
2939
- sendVideo: false,
2940
- receiveVideo: false,
2941
- sendAudio: false,
2942
- receiveAudio: false,
2943
- sendShare: false,
2944
- receiveShare: true
2945
- }
2946
- });
3457
+ assert.calledWith(meeting.reconnectionManager.reconnectMedia, {
3458
+ mediaDirection: {
3459
+ sendVideo: false,
3460
+ receiveVideo: false,
3461
+ sendAudio: false,
3462
+ receiveAudio: false,
3463
+ sendShare: false,
3464
+ receiveShare: true,
3465
+ },
3466
+ });
2947
3467
  });
2948
3468
 
2949
3469
  it('should throw an error if moveTo call fails', async () => {
2950
3470
  MeetingUtil.joinMeeting = sinon.stub().returns(Promise.reject());
2951
3471
  try {
2952
3472
  await meeting.moveTo('resourceId');
2953
- }
2954
- catch {
3473
+ } catch {
2955
3474
  assert.calledOnce(Metrics.sendBehavioralMetric);
2956
- assert.calledWith(
2957
- Metrics.sendBehavioralMetric,
2958
- BEHAVIORAL_METRICS.MOVE_TO_FAILURE,
2959
- {
2960
- correlation_id: meeting.correlationId,
2961
- locus_id: meeting.locusUrl.split('/').pop(),
2962
- reason: sinon.match.any,
2963
- stack: sinon.match.any
2964
- }
2965
- );
3475
+ assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.MOVE_TO_FAILURE, {
3476
+ correlation_id: meeting.correlationId,
3477
+ locus_id: meeting.locusUrl.split('/').pop(),
3478
+ reason: sinon.match.any,
3479
+ stack: sinon.match.any,
3480
+ });
2966
3481
  }
2967
3482
  Metrics.sendBehavioralMetric.reset();
2968
3483
  meeting.reconnectionManager.reconnectMedia = sinon.stub().returns(Promise.reject());
@@ -2972,23 +3487,18 @@ describe('plugin-meetings', () => {
2972
3487
  await meeting.locusInfo.emitScoped(
2973
3488
  {
2974
3489
  file: 'locus-info',
2975
- function: 'updateSelf'
3490
+ function: 'updateSelf',
2976
3491
  },
2977
3492
  'SELF_OBSERVING'
2978
3493
  );
2979
- }
2980
- catch {
3494
+ } catch {
2981
3495
  assert.calledOnce(Metrics.sendBehavioralMetric);
2982
- assert.calledWith(
2983
- Metrics.sendBehavioralMetric,
2984
- BEHAVIORAL_METRICS.MOVE_TO_FAILURE,
2985
- {
2986
- correlation_id: meeting.correlationId,
2987
- locus_id: meeting.locusUrl.split('/').pop(),
2988
- reason: sinon.match.any,
2989
- stack: sinon.match.any
2990
- }
2991
- );
3496
+ assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.MOVE_TO_FAILURE, {
3497
+ correlation_id: meeting.correlationId,
3498
+ locus_id: meeting.locusUrl.split('/').pop(),
3499
+ reason: sinon.match.any,
3500
+ stack: sinon.match.any,
3501
+ });
2992
3502
  }
2993
3503
  });
2994
3504
  });
@@ -2998,7 +3508,11 @@ describe('plugin-meetings', () => {
2998
3508
 
2999
3509
  beforeEach(() => {
3000
3510
  sandbox = sinon.createSandbox();
3001
- sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(MeetingUtil.parseLocusJoin({body: {locus, mediaConnections: []}})));
3511
+ sandbox
3512
+ .stub(MeetingUtil, 'joinMeeting')
3513
+ .returns(
3514
+ Promise.resolve(MeetingUtil.parseLocusJoin({body: {locus, mediaConnections: []}}))
3515
+ );
3002
3516
  sandbox.stub(MeetingUtil, 'leaveMeeting').returns(Promise.resolve());
3003
3517
  });
3004
3518
 
@@ -3010,8 +3524,7 @@ describe('plugin-meetings', () => {
3010
3524
  it('should throw an error if resourceId not passed', async () => {
3011
3525
  try {
3012
3526
  await meeting.moveFrom();
3013
- }
3014
- catch (err) {
3527
+ } catch (err) {
3015
3528
  assert.instanceOf(err, ParameterError);
3016
3529
 
3017
3530
  assert.equal(err.sdkMessage, 'Cannot move call without a resourceId.');
@@ -3032,33 +3545,25 @@ describe('plugin-meetings', () => {
3032
3545
  assert.calledWith(MeetingUtil.leaveMeeting, meeting, {
3033
3546
  resourceId: 'resourceId',
3034
3547
  correlationId: meeting.correlationId,
3035
- moveMeeting: true
3548
+ moveMeeting: true,
3036
3549
  });
3037
3550
 
3038
3551
  assert.calledOnce(Metrics.sendBehavioralMetric);
3039
- assert.calledWith(
3040
- Metrics.sendBehavioralMetric,
3041
- BEHAVIORAL_METRICS.MOVE_FROM_SUCCESS,
3042
- );
3552
+ assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.MOVE_FROM_SUCCESS);
3043
3553
  });
3044
3554
 
3045
3555
  it('should throw an error if moveFrom call fails', async () => {
3046
3556
  MeetingUtil.joinMeeting = sinon.stub().returns(Promise.reject());
3047
3557
  try {
3048
3558
  await meeting.moveFrom('resourceId');
3049
- }
3050
- catch {
3559
+ } catch {
3051
3560
  assert.calledOnce(Metrics.sendBehavioralMetric);
3052
- assert.calledWith(
3053
- Metrics.sendBehavioralMetric,
3054
- BEHAVIORAL_METRICS.MOVE_FROM_FAILURE,
3055
- {
3056
- correlation_id: meeting.correlationId,
3057
- locus_id: meeting.locusUrl.split('/').pop(),
3058
- reason: sinon.match.any,
3059
- stack: sinon.match.any
3060
- }
3061
- );
3561
+ assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.MOVE_FROM_FAILURE, {
3562
+ correlation_id: meeting.correlationId,
3563
+ locus_id: meeting.locusUrl.split('/').pop(),
3564
+ reason: sinon.match.any,
3565
+ stack: sinon.match.any,
3566
+ });
3062
3567
  }
3063
3568
  });
3064
3569
  });
@@ -3127,7 +3632,9 @@ describe('plugin-meetings', () => {
3127
3632
  meeting.config.reconnection.enabled = true;
3128
3633
  meeting.currentMediaStatus = {audio: true};
3129
3634
  meeting.reconnectionManager = new ReconnectionManager(meeting);
3130
- meeting.reconnectionManager.reconnect = sinon.stub().returns(Promise.reject(new Error()));
3635
+ meeting.reconnectionManager.reconnect = sinon
3636
+ .stub()
3637
+ .returns(Promise.reject(new Error()));
3131
3638
  meeting.reconnectionManager.reset = sinon.stub().returns(true);
3132
3639
  });
3133
3640
 
@@ -3152,7 +3659,7 @@ describe('plugin-meetings', () => {
3152
3659
  correlation_id: meeting.correlationId,
3153
3660
  locus_id: meeting.locusUrl.split('/').pop(),
3154
3661
  reason: sinon.match.any,
3155
- stack: sinon.match.any
3662
+ stack: sinon.match.any,
3156
3663
  }
3157
3664
  );
3158
3665
  });
@@ -3164,7 +3671,7 @@ describe('plugin-meetings', () => {
3164
3671
  sinon.match.instanceOf(Meeting),
3165
3672
  {file: 'meeting/index', function: 'reconnect'},
3166
3673
  EVENTS.REQUEST_UPLOAD_LOGS,
3167
- sinon.match.instanceOf(Meeting),
3674
+ sinon.match.instanceOf(Meeting)
3168
3675
  );
3169
3676
  });
3170
3677
 
@@ -3248,8 +3755,8 @@ describe('plugin-meetings', () => {
3248
3755
  height: 1980,
3249
3756
  width: 1080,
3250
3757
  displaySurface: true,
3251
- cursor: true
3252
- })
3758
+ cursor: true,
3759
+ }),
3253
3760
  };
3254
3761
  const getVideoTracks = sinon.stub().returns([track]);
3255
3762
 
@@ -3281,33 +3788,51 @@ describe('plugin-meetings', () => {
3281
3788
  // mock the on() method and store all the listeners
3282
3789
  on: sinon.stub().callsFake((event, listener) => {
3283
3790
  eventListeners[event] = listener;
3284
- })
3791
+ }),
3285
3792
  };
3286
3793
  });
3287
3794
 
3288
3795
  it('should register for all the correct RoapMediaConnection events', () => {
3289
3796
  meeting.setupMediaConnectionListeners();
3290
- assert.isFunction(eventListeners[MC.Event.ROAP_STARTED]);
3291
- assert.isFunction(eventListeners[MC.Event.ROAP_DONE]);
3292
- assert.isFunction(eventListeners[MC.Event.ROAP_FAILURE]);
3293
- assert.isFunction(eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]);
3294
- assert.isFunction(eventListeners[MC.Event.REMOTE_TRACK_ADDED]);
3295
- assert.isFunction(eventListeners[MC.Event.CONNECTION_STATE_CHANGED]);
3797
+ assert.isFunction(eventListeners[Event.ROAP_STARTED]);
3798
+ assert.isFunction(eventListeners[Event.ROAP_DONE]);
3799
+ assert.isFunction(eventListeners[Event.ROAP_FAILURE]);
3800
+ assert.isFunction(eventListeners[Event.ROAP_MESSAGE_TO_SEND]);
3801
+ assert.isFunction(eventListeners[Event.REMOTE_TRACK_ADDED]);
3802
+ assert.isFunction(eventListeners[Event.CONNECTION_STATE_CHANGED]);
3296
3803
  });
3297
3804
 
3298
3805
  it('should trigger a media:ready event when REMOTE_TRACK_ADDED is fired', () => {
3299
3806
  meeting.setupMediaConnectionListeners();
3300
- eventListeners[MC.Event.REMOTE_TRACK_ADDED]({track: 'track', type: MC.RemoteTrackType.AUDIO});
3807
+ eventListeners[Event.REMOTE_TRACK_ADDED]({
3808
+ track: 'track',
3809
+ type: RemoteTrackType.AUDIO,
3810
+ });
3301
3811
  assert.equal(TriggerProxy.trigger.getCall(1).args[2], 'media:ready');
3302
- assert.deepEqual(TriggerProxy.trigger.getCall(1).args[3], {type: 'remoteAudio', stream: true});
3812
+ assert.deepEqual(TriggerProxy.trigger.getCall(1).args[3], {
3813
+ type: 'remoteAudio',
3814
+ stream: true,
3815
+ });
3303
3816
 
3304
- eventListeners[MC.Event.REMOTE_TRACK_ADDED]({track: 'track', type: MC.RemoteTrackType.VIDEO});
3817
+ eventListeners[Event.REMOTE_TRACK_ADDED]({
3818
+ track: 'track',
3819
+ type: RemoteTrackType.VIDEO,
3820
+ });
3305
3821
  assert.equal(TriggerProxy.trigger.getCall(2).args[2], 'media:ready');
3306
- assert.deepEqual(TriggerProxy.trigger.getCall(2).args[3], {type: 'remoteVideo', stream: true});
3822
+ assert.deepEqual(TriggerProxy.trigger.getCall(2).args[3], {
3823
+ type: 'remoteVideo',
3824
+ stream: true,
3825
+ });
3307
3826
 
3308
- eventListeners[MC.Event.REMOTE_TRACK_ADDED]({track: 'track', type: MC.RemoteTrackType.SCREENSHARE_VIDEO});
3827
+ eventListeners[Event.REMOTE_TRACK_ADDED]({
3828
+ track: 'track',
3829
+ type: RemoteTrackType.SCREENSHARE_VIDEO,
3830
+ });
3309
3831
  assert.equal(TriggerProxy.trigger.getCall(3).args[2], 'media:ready');
3310
- assert.deepEqual(TriggerProxy.trigger.getCall(3).args[3], {type: 'remoteShare', stream: true});
3832
+ assert.deepEqual(TriggerProxy.trigger.getCall(3).args[3], {
3833
+ type: 'remoteShare',
3834
+ stream: true,
3835
+ });
3311
3836
  });
3312
3837
 
3313
3838
  describe('should send correct metrics for ROAP_FAILURE event', () => {
@@ -3321,10 +3846,19 @@ describe('plugin-meetings', () => {
3321
3846
 
3322
3847
  const checkMetricSent = (event) => {
3323
3848
  assert.calledOnce(Metrics.postEvent);
3324
- assert.calledWithMatch(Metrics.postEvent, {event, meetingId: meeting.id, data: {canProceed: false}});
3849
+ assert.calledWithMatch(Metrics.postEvent, {
3850
+ event,
3851
+ meetingId: meeting.id,
3852
+ data: {canProceed: false},
3853
+ });
3325
3854
  };
3326
3855
 
3327
- const checkBehavioralMetricSent = (metricName, expectedCode, expectedReason, expectedMetadataType) => {
3856
+ const checkBehavioralMetricSent = (
3857
+ metricName,
3858
+ expectedCode,
3859
+ expectedReason,
3860
+ expectedMetadataType
3861
+ ) => {
3328
3862
  assert.calledOnce(Metrics.sendBehavioralMetric);
3329
3863
  assert.calledWith(
3330
3864
  Metrics.sendBehavioralMetric,
@@ -3333,65 +3867,101 @@ describe('plugin-meetings', () => {
3333
3867
  code: expectedCode,
3334
3868
  correlation_id: meeting.correlationId,
3335
3869
  reason: expectedReason,
3336
- stack: sinon.match.any
3870
+ stack: sinon.match.any,
3337
3871
  },
3338
3872
  {
3339
- type: expectedMetadataType
3873
+ type: expectedMetadataType,
3340
3874
  }
3341
3875
  );
3342
3876
  };
3343
3877
 
3344
3878
  it('should send metrics for SdpOfferCreationError error', () => {
3345
- const fakeError = new MC.Errors.SdpOfferCreationError(fakeErrorMessage, {name: fakeErrorName, cause: {name: fakeRootCauseName}});
3879
+ const fakeError = new Errors.SdpOfferCreationError(fakeErrorMessage, {
3880
+ name: fakeErrorName,
3881
+ cause: {name: fakeRootCauseName},
3882
+ });
3346
3883
 
3347
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3884
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3348
3885
 
3349
3886
  checkMetricSent(eventType.LOCAL_SDP_GENERATED);
3350
- checkBehavioralMetricSent(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, MC.Errors.ErrorCode.SdpOfferCreationError, fakeErrorMessage, fakeRootCauseName);
3887
+ checkBehavioralMetricSent(
3888
+ BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE,
3889
+ Errors.ErrorCode.SdpOfferCreationError,
3890
+ fakeErrorMessage,
3891
+ fakeRootCauseName
3892
+ );
3351
3893
  });
3352
3894
 
3353
3895
  it('should send metrics for SdpOfferHandlingError error', () => {
3354
- const fakeError = new MC.Errors.SdpOfferHandlingError(fakeErrorMessage, {name: fakeErrorName, cause: {name: fakeRootCauseName}});
3896
+ const fakeError = new Errors.SdpOfferHandlingError(fakeErrorMessage, {
3897
+ name: fakeErrorName,
3898
+ cause: {name: fakeRootCauseName},
3899
+ });
3355
3900
 
3356
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3901
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3357
3902
 
3358
3903
  checkMetricSent(eventType.REMOTE_SDP_RECEIVED);
3359
- checkBehavioralMetricSent(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, MC.Errors.ErrorCode.SdpOfferHandlingError, fakeErrorMessage, fakeRootCauseName);
3904
+ checkBehavioralMetricSent(
3905
+ BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE,
3906
+ Errors.ErrorCode.SdpOfferHandlingError,
3907
+ fakeErrorMessage,
3908
+ fakeRootCauseName
3909
+ );
3360
3910
  });
3361
3911
 
3362
3912
  it('should send metrics for SdpAnswerHandlingError error', () => {
3363
- const fakeError = new MC.Errors.SdpAnswerHandlingError(fakeErrorMessage, {name: fakeErrorName, cause: {name: fakeRootCauseName}});
3913
+ const fakeError = new Errors.SdpAnswerHandlingError(fakeErrorMessage, {
3914
+ name: fakeErrorName,
3915
+ cause: {name: fakeRootCauseName},
3916
+ });
3364
3917
 
3365
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3918
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3366
3919
 
3367
3920
  checkMetricSent(eventType.REMOTE_SDP_RECEIVED);
3368
- checkBehavioralMetricSent(BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE, MC.Errors.ErrorCode.SdpAnswerHandlingError, fakeErrorMessage, fakeRootCauseName);
3921
+ checkBehavioralMetricSent(
3922
+ BEHAVIORAL_METRICS.PEERCONNECTION_FAILURE,
3923
+ Errors.ErrorCode.SdpAnswerHandlingError,
3924
+ fakeErrorMessage,
3925
+ fakeRootCauseName
3926
+ );
3369
3927
  });
3370
3928
 
3371
3929
  it('should send metrics for SdpError error', () => {
3372
3930
  // SdpError is usually without a cause
3373
- const fakeError = new MC.Errors.SdpError(fakeErrorMessage, {name: fakeErrorName});
3931
+ const fakeError = new Errors.SdpError(fakeErrorMessage, {name: fakeErrorName});
3374
3932
 
3375
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3933
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3376
3934
 
3377
3935
  checkMetricSent(eventType.LOCAL_SDP_GENERATED);
3378
3936
  // expectedMetadataType is the error name in this case
3379
- checkBehavioralMetricSent(BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE, MC.Errors.ErrorCode.SdpError, fakeErrorMessage, fakeErrorName);
3937
+ checkBehavioralMetricSent(
3938
+ BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE,
3939
+ Errors.ErrorCode.SdpError,
3940
+ fakeErrorMessage,
3941
+ fakeErrorName
3942
+ );
3380
3943
  });
3381
3944
 
3382
3945
  it('should send metrics for IceGatheringError error', () => {
3383
3946
  // IceGatheringError is usually without a cause
3384
- const fakeError = new MC.Errors.IceGatheringError(fakeErrorMessage, {name: fakeErrorName});
3947
+ const fakeError = new Errors.IceGatheringError(fakeErrorMessage, {
3948
+ name: fakeErrorName,
3949
+ });
3385
3950
 
3386
- eventListeners[MC.Event.ROAP_FAILURE](fakeError);
3951
+ eventListeners[Event.ROAP_FAILURE](fakeError);
3387
3952
 
3388
3953
  checkMetricSent(eventType.LOCAL_SDP_GENERATED);
3389
3954
  // expectedMetadataType is the error name in this case
3390
- checkBehavioralMetricSent(BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE, MC.Errors.ErrorCode.IceGatheringError, fakeErrorMessage, fakeErrorName);
3955
+ checkBehavioralMetricSent(
3956
+ BEHAVIORAL_METRICS.INVALID_ICE_CANDIDATE,
3957
+ Errors.ErrorCode.IceGatheringError,
3958
+ fakeErrorMessage,
3959
+ fakeErrorName
3960
+ );
3391
3961
  });
3392
3962
  });
3393
3963
 
3394
- describe('handles MC.Event.ROAP_MESSAGE_TO_SEND correctly', () => {
3964
+ describe('handles Event.ROAP_MESSAGE_TO_SEND correctly', () => {
3395
3965
  let sendRoapOKStub;
3396
3966
  let sendRoapMediaRequestStub;
3397
3967
  let sendRoapAnswerStub;
@@ -3399,7 +3969,9 @@ describe('plugin-meetings', () => {
3399
3969
 
3400
3970
  beforeEach(() => {
3401
3971
  sendRoapOKStub = sinon.stub(meeting.roap, 'sendRoapOK').resolves({});
3402
- sendRoapMediaRequestStub = sinon.stub(meeting.roap, 'sendRoapMediaRequest').resolves({});
3972
+ sendRoapMediaRequestStub = sinon
3973
+ .stub(meeting.roap, 'sendRoapMediaRequest')
3974
+ .resolves({});
3403
3975
  sendRoapAnswerStub = sinon.stub(meeting.roap, 'sendRoapAnswer').resolves({});
3404
3976
  sendRoapErrorStub = sinon.stub(meeting.roap, 'sendRoapError').resolves({});
3405
3977
 
@@ -3407,106 +3979,140 @@ describe('plugin-meetings', () => {
3407
3979
  });
3408
3980
 
3409
3981
  it('handles OK message correctly', () => {
3410
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({roapMessage: {messageType: 'OK', seq: 1}});
3982
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3983
+ roapMessage: {messageType: 'OK', seq: 1},
3984
+ });
3411
3985
 
3412
3986
  assert.calledOnce(Metrics.postEvent);
3413
- assert.calledWithMatch(Metrics.postEvent, {event: eventType.REMOTE_SDP_RECEIVED, meetingId: meeting.id});
3987
+ assert.calledWithMatch(Metrics.postEvent, {
3988
+ event: eventType.REMOTE_SDP_RECEIVED,
3989
+ meetingId: meeting.id,
3990
+ });
3414
3991
 
3415
3992
  assert.calledOnce(sendRoapOKStub);
3416
- assert.calledWith(sendRoapOKStub, {seq: 1, mediaId: meeting.mediaId, correlationId: meeting.correlationId});
3993
+ assert.calledWith(sendRoapOKStub, {
3994
+ seq: 1,
3995
+ mediaId: meeting.mediaId,
3996
+ correlationId: meeting.correlationId,
3997
+ });
3417
3998
  });
3418
3999
 
3419
4000
  it('handles OFFER message correctly', () => {
3420
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
4001
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3421
4002
  roapMessage: {
3422
4003
  messageType: 'OFFER',
3423
4004
  seq: 1,
3424
4005
  sdp: 'fake sdp',
3425
4006
  tieBreaker: 12345,
3426
- }
4007
+ },
3427
4008
  });
3428
4009
 
3429
4010
  assert.calledOnce(Metrics.postEvent);
3430
- assert.calledWithMatch(Metrics.postEvent, {event: eventType.LOCAL_SDP_GENERATED, meetingId: meeting.id});
4011
+ assert.calledWithMatch(Metrics.postEvent, {
4012
+ event: eventType.LOCAL_SDP_GENERATED,
4013
+ meetingId: meeting.id,
4014
+ });
3431
4015
 
3432
4016
  assert.calledOnce(sendRoapMediaRequestStub);
3433
4017
  assert.calledWith(sendRoapMediaRequestStub, {
3434
- seq: 1, sdp: 'fake sdp', tieBreaker: 12345, meeting, reconnect: false
4018
+ seq: 1,
4019
+ sdp: 'fake sdp',
4020
+ tieBreaker: 12345,
4021
+ meeting,
4022
+ reconnect: false,
3435
4023
  });
3436
4024
  });
3437
4025
 
3438
4026
  it('handles ANSWER message correctly', () => {
3439
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
4027
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3440
4028
  roapMessage: {
3441
4029
  messageType: 'ANSWER',
3442
4030
  seq: 10,
3443
4031
  sdp: 'fake sdp answer',
3444
4032
  tieBreaker: 12345,
3445
- }
4033
+ },
3446
4034
  });
3447
4035
 
3448
4036
  assert.calledOnce(Metrics.postEvent);
3449
- assert.calledWithMatch(Metrics.postEvent, {event: eventType.REMOTE_SDP_RECEIVED, meetingId: meeting.id});
4037
+ assert.calledWithMatch(Metrics.postEvent, {
4038
+ event: eventType.REMOTE_SDP_RECEIVED,
4039
+ meetingId: meeting.id,
4040
+ });
3450
4041
 
3451
4042
  assert.calledOnce(sendRoapAnswerStub);
3452
4043
  assert.calledWith(sendRoapAnswerStub, {
3453
- seq: 10, sdp: 'fake sdp answer', mediaId: meeting.mediaId, correlationId: meeting.correlationId
4044
+ seq: 10,
4045
+ sdp: 'fake sdp answer',
4046
+ mediaId: meeting.mediaId,
4047
+ correlationId: meeting.correlationId,
3454
4048
  });
3455
4049
  });
3456
4050
 
3457
4051
  it('sends metrics if fails to send roap ANSWER message', async () => {
3458
4052
  sendRoapAnswerStub.rejects(new Error('sending answer failed'));
3459
4053
 
3460
- await eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
4054
+ await eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3461
4055
  roapMessage: {
3462
4056
  messageType: 'ANSWER',
3463
4057
  seq: 10,
3464
4058
  sdp: 'fake sdp answer',
3465
4059
  tieBreaker: 12345,
3466
- }
4060
+ },
3467
4061
  });
3468
4062
  await testUtils.flushPromises();
3469
4063
 
3470
4064
  assert.calledOnce(Metrics.sendBehavioralMetric);
3471
- assert.calledWithMatch(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ROAP_ANSWER_FAILURE, {
3472
- correlation_id: meeting.correlationId,
3473
- locus_id: meeting.locusUrl.split('/').pop(),
3474
- reason: 'sending answer failed'
3475
- });
4065
+ assert.calledWithMatch(
4066
+ Metrics.sendBehavioralMetric,
4067
+ BEHAVIORAL_METRICS.ROAP_ANSWER_FAILURE,
4068
+ {
4069
+ correlation_id: meeting.correlationId,
4070
+ locus_id: meeting.locusUrl.split('/').pop(),
4071
+ reason: 'sending answer failed',
4072
+ }
4073
+ );
3476
4074
  });
3477
4075
 
3478
- [MC.ErrorType.CONFLICT, MC.ErrorType.DOUBLECONFLICT].forEach((errorType) =>
4076
+ [ErrorType.CONFLICT, ErrorType.DOUBLECONFLICT].forEach((errorType) =>
3479
4077
  it(`handles ERROR message indicating glare condition correctly (errorType=${errorType})`, () => {
3480
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
4078
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3481
4079
  roapMessage: {
3482
4080
  messageType: 'ERROR',
3483
4081
  seq: 10,
3484
4082
  errorType,
3485
4083
  tieBreaker: 12345,
3486
- }
4084
+ },
3487
4085
  });
3488
4086
 
3489
4087
  assert.calledOnce(Metrics.sendBehavioralMetric);
3490
- assert.calledWithMatch(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ROAP_GLARE_CONDITION, {
3491
- correlation_id: meeting.correlationId,
3492
- locus_id: meeting.locusUrl.split('/').pop(),
3493
- sequence: 10
3494
- });
4088
+ assert.calledWithMatch(
4089
+ Metrics.sendBehavioralMetric,
4090
+ BEHAVIORAL_METRICS.ROAP_GLARE_CONDITION,
4091
+ {
4092
+ correlation_id: meeting.correlationId,
4093
+ locus_id: meeting.locusUrl.split('/').pop(),
4094
+ sequence: 10,
4095
+ }
4096
+ );
3495
4097
 
3496
4098
  assert.calledOnce(sendRoapErrorStub);
3497
4099
  assert.calledWith(sendRoapErrorStub, {
3498
- seq: 10, errorType, mediaId: meeting.mediaId, correlationId: meeting.correlationId
4100
+ seq: 10,
4101
+ errorType,
4102
+ mediaId: meeting.mediaId,
4103
+ correlationId: meeting.correlationId,
3499
4104
  });
3500
- }));
4105
+ })
4106
+ );
3501
4107
 
3502
4108
  it('handles ERROR message indicating other errors correctly', () => {
3503
- eventListeners[MC.Event.ROAP_MESSAGE_TO_SEND]({
4109
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
3504
4110
  roapMessage: {
3505
4111
  messageType: 'ERROR',
3506
4112
  seq: 10,
3507
- errorType: MC.ErrorType.FAILED,
4113
+ errorType: ErrorType.FAILED,
3508
4114
  tieBreaker: 12345,
3509
- }
4115
+ },
3510
4116
  });
3511
4117
 
3512
4118
  assert.notCalled(Metrics.sendBehavioralMetric);
@@ -3514,9 +4120,9 @@ describe('plugin-meetings', () => {
3514
4120
  assert.calledOnce(sendRoapErrorStub);
3515
4121
  assert.calledWith(sendRoapErrorStub, {
3516
4122
  seq: 10,
3517
- errorType: MC.ErrorType.FAILED,
4123
+ errorType: ErrorType.FAILED,
3518
4124
  mediaId: meeting.mediaId,
3519
- correlationId: meeting.correlationId
4125
+ correlationId: meeting.correlationId,
3520
4126
  });
3521
4127
  });
3522
4128
  });
@@ -3550,6 +4156,83 @@ describe('plugin-meetings', () => {
3550
4156
  );
3551
4157
  done();
3552
4158
  });
4159
+
4160
+ it('listens to the breakouts changed event', () => {
4161
+ meeting.breakouts.updateBreakoutSessions = sinon.stub();
4162
+
4163
+ const payload = 'payload';
4164
+
4165
+ meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_MEETING_BREAKOUTS_CHANGED', payload);
4166
+
4167
+ assert.calledOnceWithExactly(meeting.breakouts.updateBreakoutSessions, payload);
4168
+ assert.calledWith(
4169
+ TriggerProxy.trigger,
4170
+ meeting,
4171
+ {file: 'meeting/index', function: 'setUpLocusInfoSelfListener'},
4172
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_UPDATE
4173
+ );
4174
+ });
4175
+ });
4176
+
4177
+ describe('#setUpBreakoutsListener', () => {
4178
+ it('listens to the closing event from breakouts and triggers the closing event', () => {
4179
+ TriggerProxy.trigger.reset();
4180
+ meeting.breakouts.trigger('BREAKOUTS_CLOSING');
4181
+
4182
+ assert.calledWith(
4183
+ TriggerProxy.trigger,
4184
+ meeting,
4185
+ {file: 'meeting/index', function: 'setUpBreakoutsListener'},
4186
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_CLOSING
4187
+ );
4188
+ });
4189
+
4190
+ it('listens to the message event from breakouts and triggers the message event', () => {
4191
+ TriggerProxy.trigger.reset();
4192
+
4193
+ const messageEvent = 'message';
4194
+
4195
+ meeting.breakouts.trigger('MESSAGE', messageEvent);
4196
+
4197
+ assert.calledWith(
4198
+ TriggerProxy.trigger,
4199
+ meeting,
4200
+ {file: 'meeting/index', function: 'setUpBreakoutsListener'},
4201
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_MESSAGE,
4202
+ messageEvent
4203
+ );
4204
+ });
4205
+
4206
+ it('listens to the members update event from breakouts and triggers the breakouts update event', () => {
4207
+ TriggerProxy.trigger.reset();
4208
+ meeting.breakouts.trigger('MEMBERS_UPDATE');
4209
+
4210
+ assert.calledWith(
4211
+ TriggerProxy.trigger,
4212
+ meeting,
4213
+ {file: 'meeting/index', function: 'setUpBreakoutsListener'},
4214
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_UPDATE
4215
+ );
4216
+ });
4217
+ });
4218
+
4219
+ describe('#setupLocusControlsListener', () => {
4220
+ it('listens to the locus breakouts update event', () => {
4221
+ const locus = {
4222
+ breakout: 'breakout'
4223
+ };
4224
+
4225
+ meeting.breakouts.updateBreakout = sinon.stub();
4226
+ meeting.locusInfo.emit({function: 'test', file: 'test'}, 'CONTROLS_MEETING_BREAKOUT_UPDATED', locus);
4227
+
4228
+ assert.calledOnceWithExactly(meeting.breakouts.updateBreakout, locus.breakout);
4229
+ assert.calledWith(
4230
+ TriggerProxy.trigger,
4231
+ meeting,
4232
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
4233
+ EVENT_TRIGGERS.MEETING_BREAKOUTS_UPDATE
4234
+ );
4235
+ });
3553
4236
  });
3554
4237
 
3555
4238
  describe('#setUpLocusUrlListener', () => {
@@ -3557,21 +4240,55 @@ describe('plugin-meetings', () => {
3557
4240
  const newLocusUrl = 'newLocusUrl/12345';
3558
4241
 
3559
4242
  meeting.members = {locusUrlUpdate: sinon.stub().returns(Promise.resolve(test1))};
4243
+ meeting.recordingController = {setLocusUrl: sinon.stub().returns(undefined)};
4244
+
4245
+ meeting.breakouts.locusUrlUpdate = sinon.stub();
3560
4246
 
3561
4247
  meeting.locusInfo.emit({function: 'test', file: 'test'}, 'LOCUS_INFO_UPDATE_URL', newLocusUrl);
3562
4248
  assert.calledWith(
3563
4249
  meeting.members.locusUrlUpdate,
3564
4250
  newLocusUrl
3565
4251
  );
4252
+ assert.calledOnceWithExactly(meeting.breakouts.locusUrlUpdate, newLocusUrl);
4253
+ assert.calledWith(meeting.members.locusUrlUpdate, newLocusUrl);
4254
+ assert.calledWith(meeting.recordingController.setLocusUrl, newLocusUrl);
3566
4255
  assert.equal(meeting.locusUrl, newLocusUrl);
3567
4256
  assert(meeting.locusId, '12345');
3568
4257
  done();
3569
4258
  });
3570
4259
  });
4260
+
4261
+ describe('#setUpLocusServicesListener', () => {
4262
+ it('listens to the locus services update event', (done) => {
4263
+ const newLocusServices = {
4264
+ services: {
4265
+ record: {
4266
+ url: 'url',
4267
+ }
4268
+ },
4269
+ };
4270
+
4271
+ meeting.recordingController = {setServiceUrl: sinon.stub().returns(undefined), setSessionId: sinon.stub().returns(undefined)};
4272
+
4273
+ meeting.locusInfo.emit(
4274
+ {function: 'test', file: 'test'},
4275
+ 'LINKS_SERVICES',
4276
+ newLocusServices
4277
+ );
4278
+
4279
+ assert.calledWith(meeting.recordingController.setServiceUrl, newLocusServices.services.record.url);
4280
+ assert.calledOnce(meeting.recordingController.setSessionId);
4281
+ done();
4282
+ });
4283
+ });
3571
4284
  describe('#setUpLocusInfoMediaInactiveListener', () => {
3572
4285
  it('listens to disconnect due to un activity ', (done) => {
3573
4286
  TriggerProxy.trigger.reset();
3574
- meeting.locusInfo.emit({function: 'test', file: 'test'}, EVENTS.DISCONNECT_DUE_TO_INACTIVITY, {reason: 'inactive'});
4287
+ meeting.locusInfo.emit(
4288
+ {function: 'test', file: 'test'},
4289
+ EVENTS.DISCONNECT_DUE_TO_INACTIVITY,
4290
+ {reason: 'inactive'}
4291
+ );
3575
4292
  assert.calledTwice(TriggerProxy.trigger);
3576
4293
 
3577
4294
  assert.calledWith(
@@ -3598,7 +4315,11 @@ describe('plugin-meetings', () => {
3598
4315
  sinon.stub(meeting, 'reconnect');
3599
4316
 
3600
4317
  meeting.config.reconnection.autoRejoin = true;
3601
- meeting.locusInfo.emit({function: 'test', file: 'test'}, EVENTS.DISCONNECT_DUE_TO_INACTIVITY, {reason: 'inactive'});
4318
+ meeting.locusInfo.emit(
4319
+ {function: 'test', file: 'test'},
4320
+ EVENTS.DISCONNECT_DUE_TO_INACTIVITY,
4321
+ {reason: 'inactive'}
4322
+ );
3602
4323
  assert.calledOnce(TriggerProxy.trigger);
3603
4324
 
3604
4325
  assert.calledWith(
@@ -3623,7 +4344,10 @@ describe('plugin-meetings', () => {
3623
4344
  sinon.stub(meeting.reconnectionManager, 'cleanUp');
3624
4345
  sinon.spy(MeetingUtil, 'cleanUp');
3625
4346
 
3626
- meeting.locusInfo.emit({function: 'test', file: 'test'}, EVENTS.DESTROY_MEETING, {shouldLeave: false, reason: 'ended'});
4347
+ meeting.locusInfo.emit({function: 'test', file: 'test'}, EVENTS.DESTROY_MEETING, {
4348
+ shouldLeave: false,
4349
+ reason: 'ended',
4350
+ });
3627
4351
  assert.calledOnce(TriggerProxy.trigger);
3628
4352
  assert.calledOnce(MeetingUtil.cleanUp);
3629
4353
  assert.calledWith(
@@ -3631,12 +4355,12 @@ describe('plugin-meetings', () => {
3631
4355
  meeting,
3632
4356
  {
3633
4357
  file: 'meeting/index',
3634
- function: 'setUpLocusInfoMeetingListener'
4358
+ function: 'setUpLocusInfoMeetingListener',
3635
4359
  },
3636
4360
  EVENTS.DESTROY_MEETING,
3637
4361
  {
3638
4362
  reason: 'ended',
3639
- meetingId: meeting.id
4363
+ meetingId: meeting.id,
3640
4364
  }
3641
4365
  );
3642
4366
  done();
@@ -3656,22 +4380,36 @@ describe('plugin-meetings', () => {
3656
4380
  sandbox = null;
3657
4381
  });
3658
4382
 
3659
- describe('#stopFloorRequest', () => {
3660
- it('should have #stopFloorRequest', () => {
3661
- assert.exists(meeting.stopFloorRequest);
4383
+ describe('#releaseScreenShareFloor', () => {
4384
+ it('should have #releaseScreenShareFloor', () => {
4385
+ assert.exists(meeting.releaseScreenShareFloor);
3662
4386
  });
3663
4387
  beforeEach(() => {
3664
- meeting.locusInfo.mediaShares = [{name: 'content', url: url1}];
4388
+ meeting.selfId = 'some self id';
4389
+ meeting.locusInfo.mediaShares = [
4390
+ {name: 'content', url: url1, floor: {beneficiary: {id: meeting.selfId}}},
4391
+ ];
3665
4392
  meeting.locusInfo.self = {url: url2};
4393
+ meeting.mediaProperties = {mediaDirection: {sendShare: true}};
3666
4394
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
3667
4395
  });
3668
- it('should call change meeting floor', async () => {
3669
- const share = meeting.share();
4396
+ it('should call changeMeetingFloor()', async () => {
4397
+ const share = meeting.releaseScreenShareFloor();
3670
4398
 
3671
4399
  assert.exists(share.then);
3672
4400
  await share;
3673
4401
  assert.calledOnce(meeting.meetingRequest.changeMeetingFloor);
3674
4402
  });
4403
+ it('should not call changeMeetingFloor() if someone else already has the floor', async () => {
4404
+ // change selfId so that it doesn't match the beneficiary id from meeting.locusInfo.mediaShares
4405
+ meeting.selfId = 'new self id';
4406
+
4407
+ const share = meeting.releaseScreenShareFloor();
4408
+
4409
+ assert.exists(share.then);
4410
+ await share;
4411
+ assert.notCalled(meeting.meetingRequest.changeMeetingFloor);
4412
+ });
3675
4413
  });
3676
4414
 
3677
4415
  describe('#setSipUri', () => {
@@ -3753,8 +4491,8 @@ describe('plugin-meetings', () => {
3753
4491
  permissionToken: 'abc',
3754
4492
  sipMeetingUri: test1,
3755
4493
  sipUrl: test1,
3756
- owner: test2
3757
- }
4494
+ owner: test2,
4495
+ },
3758
4496
  };
3759
4497
 
3760
4498
  meeting.parseMeetingInfo(FAKE_MEETING_INFO);
@@ -3765,7 +4503,7 @@ describe('plugin-meetings', () => {
3765
4503
  meetingNumber: '12345',
3766
4504
  meetingJoinUrl: url2,
3767
4505
  owner: test2,
3768
- permissionToken: 'abc'
4506
+ permissionToken: 'abc',
3769
4507
  };
3770
4508
 
3771
4509
  checkParseMeetingInfo(expectedInfoToParse);
@@ -3779,8 +4517,8 @@ describe('plugin-meetings', () => {
3779
4517
  info: {
3780
4518
  webExMeetingId: 'locusMeetingId',
3781
4519
  sipUri: 'locusSipUri',
3782
- owner: 'locusOwner'
3783
- }
4520
+ owner: 'locusOwner',
4521
+ },
3784
4522
  };
3785
4523
  const FAKE_MEETING_INFO = {
3786
4524
  body: {
@@ -3791,8 +4529,8 @@ describe('plugin-meetings', () => {
3791
4529
  permissionToken: 'abc',
3792
4530
  sipMeetingUri: test1,
3793
4531
  sipUrl: test1,
3794
- owner: test2
3795
- }
4532
+ owner: test2,
4533
+ },
3796
4534
  };
3797
4535
 
3798
4536
  meeting.parseMeetingInfo(FAKE_MEETING_INFO, FAKE_LOCUS_MEETING);
@@ -3803,7 +4541,7 @@ describe('plugin-meetings', () => {
3803
4541
  meetingNumber: 'locusMeetingId',
3804
4542
  meetingJoinUrl: url2,
3805
4543
  owner: 'locusOwner',
3806
- permissionToken: 'abc'
4544
+ permissionToken: 'abc',
3807
4545
  };
3808
4546
 
3809
4547
  checkParseMeetingInfo(expectedInfoToParse);
@@ -3820,8 +4558,8 @@ describe('plugin-meetings', () => {
3820
4558
  permissionToken: 'abc',
3821
4559
  sipMeetingUri: test1,
3822
4560
  sipUrl: test1,
3823
- owner: test2
3824
- }
4561
+ owner: test2,
4562
+ },
3825
4563
  };
3826
4564
 
3827
4565
  meeting.parseMeetingInfo(FAKE_MEETING_INFO);
@@ -3832,7 +4570,7 @@ describe('plugin-meetings', () => {
3832
4570
  meetingNumber: '12345',
3833
4571
  meetingJoinUrl: url2,
3834
4572
  owner: test2,
3835
- permissionToken: 'abc'
4573
+ permissionToken: 'abc',
3836
4574
  };
3837
4575
 
3838
4576
  checkParseMeetingInfo(expectedInfoToParse);
@@ -3850,8 +4588,8 @@ describe('plugin-meetings', () => {
3850
4588
  permissionToken: 'abc',
3851
4589
  sipMeetingUri: test1,
3852
4590
  sipUrl: test1,
3853
- owner: test2
3854
- }
4591
+ owner: test2,
4592
+ },
3855
4593
  };
3856
4594
 
3857
4595
  meeting.parseMeetingInfo(FAKE_MEETING_INFO, FAKE_STRING_DESTINATION);
@@ -3862,7 +4600,7 @@ describe('plugin-meetings', () => {
3862
4600
  meetingNumber: '12345',
3863
4601
  meetingJoinUrl: url2,
3864
4602
  owner: test2,
3865
- permissionToken: 'abc'
4603
+ permissionToken: 'abc',
3866
4604
  };
3867
4605
 
3868
4606
  checkParseMeetingInfo(expectedInfoToParse);
@@ -3878,7 +4616,11 @@ describe('plugin-meetings', () => {
3878
4616
  meeting.type = 'CALL';
3879
4617
  meeting.parseLocus({url: url1, participants: [{id: uuid1}], self: {id: uuid2}});
3880
4618
  assert.calledOnce(meeting.setLocus);
3881
- assert.calledWith(meeting.setLocus, {url: url1, participants: [{id: uuid1}], self: {id: uuid2}});
4619
+ assert.calledWith(meeting.setLocus, {
4620
+ url: url1,
4621
+ participants: [{id: uuid1}],
4622
+ self: {id: uuid2},
4623
+ });
3882
4624
  assert.calledOnce(MeetingUtil.getLocusPartner);
3883
4625
  assert.calledWith(MeetingUtil.getLocusPartner, [{id: uuid1}], {id: uuid2});
3884
4626
  assert.deepEqual(meeting.partner, {person: {sipUrl: uuid3}});
@@ -3927,7 +4669,7 @@ describe('plugin-meetings', () => {
3927
4669
  meeting,
3928
4670
  {
3929
4671
  file: 'meeting/index',
3930
- function: 'setUpLocusInfoAssignHostListener'
4672
+ function: 'setUpLocusInfoAssignHostListener',
3931
4673
  },
3932
4674
  'meeting:actionsUpdate',
3933
4675
  meeting.inMeetingActions.get()
@@ -3946,7 +4688,7 @@ describe('plugin-meetings', () => {
3946
4688
  let inMeetingActionsSetSpy;
3947
4689
  let canUserLockSpy;
3948
4690
  let canUserUnlockSpy;
3949
- let canUserRecordSpy;
4691
+ let canUserStartSpy;
3950
4692
  let canUserStopSpy;
3951
4693
  let canUserPauseSpy;
3952
4694
  let canUserResumeSpy;
@@ -3955,21 +4697,30 @@ describe('plugin-meetings', () => {
3955
4697
  let canUserLowerAllHandsSpy;
3956
4698
  let canUserLowerSomeoneElsesHandSpy;
3957
4699
  let waitingForOthersToJoinSpy;
4700
+ let handleDataChannelUrlChangeSpy;
4701
+ let canEnableReactionsSpy;
4702
+ let canSendReactionsSpy;
3958
4703
 
3959
4704
  beforeEach(() => {
3960
4705
  locusInfoOnSpy = sinon.spy(meeting.locusInfo, 'on');
3961
4706
  canUserLockSpy = sinon.spy(MeetingUtil, 'canUserLock');
3962
4707
  canUserUnlockSpy = sinon.spy(MeetingUtil, 'canUserUnlock');
3963
- canUserRecordSpy = sinon.spy(MeetingUtil, 'canUserRecord');
3964
- canUserStopSpy = sinon.spy(MeetingUtil, 'canUserStop');
3965
- canUserPauseSpy = sinon.spy(MeetingUtil, 'canUserPause');
3966
- canUserResumeSpy = sinon.spy(MeetingUtil, 'canUserResume');
4708
+ canUserStartSpy = sinon.spy(RecordingUtil, 'canUserStart');
4709
+ canUserStopSpy = sinon.spy(RecordingUtil, 'canUserStop');
4710
+ canUserPauseSpy = sinon.spy(RecordingUtil, 'canUserPause');
4711
+ canUserResumeSpy = sinon.spy(RecordingUtil, 'canUserResume');
3967
4712
  inMeetingActionsSetSpy = sinon.spy(meeting.inMeetingActions, 'set');
3968
4713
  canUserRaiseHandSpy = sinon.spy(MeetingUtil, 'canUserRaiseHand');
3969
4714
  canUserLowerAllHandsSpy = sinon.spy(MeetingUtil, 'canUserLowerAllHands');
3970
- bothLeaveAndEndMeetingAvailableSpy = sinon.spy(MeetingUtil, 'bothLeaveAndEndMeetingAvailable');
4715
+ bothLeaveAndEndMeetingAvailableSpy = sinon.spy(
4716
+ MeetingUtil,
4717
+ 'bothLeaveAndEndMeetingAvailable'
4718
+ );
3971
4719
  canUserLowerSomeoneElsesHandSpy = sinon.spy(MeetingUtil, 'canUserLowerSomeoneElsesHand');
3972
4720
  waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
4721
+ handleDataChannelUrlChangeSpy = sinon.spy(meeting, 'handleDataChannelUrlChange');
4722
+ canEnableReactionsSpy = sinon.spy(MeetingUtil, 'canEnableReactions');
4723
+ canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
3973
4724
  });
3974
4725
 
3975
4726
  afterEach(() => {
@@ -3978,7 +4729,6 @@ describe('plugin-meetings', () => {
3978
4729
  waitingForOthersToJoinSpy.restore();
3979
4730
  });
3980
4731
 
3981
-
3982
4732
  it('registers the correct MEETING_INFO_UPDATED event', () => {
3983
4733
  meeting.setUpLocusInfoMeetingInfoListener();
3984
4734
 
@@ -3991,15 +4741,16 @@ describe('plugin-meetings', () => {
3991
4741
 
3992
4742
  const payload = {
3993
4743
  info: {
3994
- userDisplayHints: ['LOCK_CONTROL_UNLOCK']
3995
- }
4744
+ userDisplayHints: ['LOCK_CONTROL_UNLOCK'],
4745
+ datachannelUrl: 'some url',
4746
+ },
3996
4747
  };
3997
4748
 
3998
4749
  callback(payload);
3999
4750
 
4000
4751
  assert.calledWith(canUserLockSpy, payload.info.userDisplayHints);
4001
4752
  assert.calledWith(canUserUnlockSpy, payload.info.userDisplayHints);
4002
- assert.calledWith(canUserRecordSpy, payload.info.userDisplayHints);
4753
+ assert.calledWith(canUserStartSpy, payload.info.userDisplayHints);
4003
4754
  assert.calledWith(canUserStopSpy, payload.info.userDisplayHints);
4004
4755
  assert.calledWith(canUserPauseSpy, payload.info.userDisplayHints);
4005
4756
  assert.calledWith(canUserResumeSpy, payload.info.userDisplayHints);
@@ -4008,13 +4759,16 @@ describe('plugin-meetings', () => {
4008
4759
  assert.calledWith(canUserLowerAllHandsSpy, payload.info.userDisplayHints);
4009
4760
  assert.calledWith(canUserLowerSomeoneElsesHandSpy, payload.info.userDisplayHints);
4010
4761
  assert.calledWith(waitingForOthersToJoinSpy, payload.info.userDisplayHints);
4762
+ assert.calledWith(handleDataChannelUrlChangeSpy, payload.info.datachannelUrl);
4763
+ assert.calledWith(canEnableReactionsSpy, null, payload.info.userDisplayHints);
4764
+ assert.calledWith(canSendReactionsSpy, null, payload.info.userDisplayHints);
4011
4765
 
4012
4766
  assert.calledWith(
4013
4767
  TriggerProxy.trigger,
4014
4768
  meeting,
4015
4769
  {
4016
4770
  file: 'meeting/index',
4017
- function: 'setUpLocusInfoMeetingInfoListener'
4771
+ function: 'setUpLocusInfoMeetingInfoListener',
4018
4772
  },
4019
4773
  'meeting:actionsUpdate',
4020
4774
  meeting.inMeetingActions.get()
@@ -4028,6 +4782,123 @@ describe('plugin-meetings', () => {
4028
4782
  });
4029
4783
  });
4030
4784
 
4785
+ describe('#handleDataChannelUrlChange', () => {
4786
+ let updateLLMConnectionSpy;
4787
+
4788
+ beforeEach(() => {
4789
+ updateLLMConnectionSpy = sinon.spy(meeting, 'updateLLMConnection');
4790
+ });
4791
+
4792
+ const check = async (url, expectedCalled) => {
4793
+ meeting.handleDataChannelUrlChange(url);
4794
+
4795
+ assert.notCalled(updateLLMConnectionSpy);
4796
+
4797
+ await testUtils.waitUntil(0);
4798
+
4799
+ if (expectedCalled) {
4800
+ assert.calledWith(updateLLMConnectionSpy);
4801
+ } else {
4802
+ assert.notCalled(updateLLMConnectionSpy);
4803
+ }
4804
+ };
4805
+
4806
+ it('calls deferred updateLLMConnection if datachannelURL is set and the enableAutomaticLLM is true', async () => {
4807
+ meeting.config.enableAutomaticLLM = true;
4808
+ check('some url', true);
4809
+ });
4810
+
4811
+ it('does not call updateLLMConnection if datachannelURL is undefined', async () => {
4812
+ meeting.config.enableAutomaticLLM = true;
4813
+ check(undefined, false);
4814
+ });
4815
+
4816
+ it('does not call updateLLMConnection if enableAutomaticLLM is false', async () => {
4817
+ check('some url', false);
4818
+ });
4819
+ });
4820
+
4821
+ describe('#updateLLMConnection', () => {
4822
+ beforeEach(() => {
4823
+ webex.internal.llm.isConnected = sinon.stub().returns(false);
4824
+ webex.internal.llm.getLocusUrl = sinon.stub();
4825
+ webex.internal.llm.registerAndConnect = sinon
4826
+ .stub()
4827
+ .returns(Promise.resolve('something'));
4828
+ webex.internal.llm.disconnectLLM = sinon.stub().returns(Promise.resolve());
4829
+ });
4830
+
4831
+ it('does not connect if the call is not joined yet', async () => {
4832
+ meeting.joinedWith = {state: 'any other state'};
4833
+ webex.internal.llm.getLocusUrl.returns('a url');
4834
+
4835
+ meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
4836
+
4837
+ const result = await meeting.updateLLMConnection();
4838
+
4839
+ assert.notCalled(webex.internal.llm.registerAndConnect);
4840
+ assert.notCalled(webex.internal.llm.disconnectLLM);
4841
+ assert.equal(result, undefined);
4842
+ });
4843
+
4844
+ it('returns undefined if llm is already connected and the locus url is unchanged', async () => {
4845
+ meeting.joinedWith = {state: 'JOINED'};
4846
+ webex.internal.llm.isConnected.returns(true);
4847
+ webex.internal.llm.getLocusUrl.returns('a url');
4848
+
4849
+ meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
4850
+
4851
+ const result = await meeting.updateLLMConnection();
4852
+
4853
+ assert.notCalled(webex.internal.llm.registerAndConnect);
4854
+ assert.notCalled(webex.internal.llm.disconnectLLM);
4855
+ assert.equal(result, undefined);
4856
+ });
4857
+
4858
+ it('connects if not already connected', async () => {
4859
+ meeting.joinedWith = {state: 'JOINED'};
4860
+ meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
4861
+
4862
+ const result = await meeting.updateLLMConnection();
4863
+
4864
+ assert.notCalled(webex.internal.llm.disconnectLLM);
4865
+ assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a datachannel url');
4866
+ assert.equal(result, 'something');
4867
+ });
4868
+
4869
+ it('disconnects if first if the locus url has changed', async () => {
4870
+ meeting.joinedWith = {state: 'JOINED'};
4871
+ webex.internal.llm.isConnected.returns(true);
4872
+ webex.internal.llm.getLocusUrl.returns('a url');
4873
+
4874
+ meeting.locusInfo = {url: 'a different url', info: {datachannelUrl: 'a datachannel url'}};
4875
+
4876
+ const result = await meeting.updateLLMConnection();
4877
+
4878
+ assert.calledWith(webex.internal.llm.disconnectLLM);
4879
+ assert.calledWith(
4880
+ webex.internal.llm.registerAndConnect,
4881
+ 'a different url',
4882
+ 'a datachannel url'
4883
+ );
4884
+ assert.equal(result, 'something');
4885
+ });
4886
+
4887
+ it('disconnects when the state is not JOINED', async () => {
4888
+ meeting.joinedWith = {state: 'any other state'};
4889
+ webex.internal.llm.isConnected.returns(true);
4890
+ webex.internal.llm.getLocusUrl.returns('a url');
4891
+
4892
+ meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
4893
+
4894
+ const result = await meeting.updateLLMConnection();
4895
+
4896
+ assert.calledWith(webex.internal.llm.disconnectLLM);
4897
+ assert.notCalled(webex.internal.llm.registerAndConnect);
4898
+ assert.equal(result, undefined);
4899
+ });
4900
+ });
4901
+
4031
4902
  describe('#setLocus', () => {
4032
4903
  beforeEach(() => {
4033
4904
  meeting.locusInfo.initialSetup = sinon.stub().returns(true);
@@ -4039,7 +4910,7 @@ describe('plugin-meetings', () => {
4039
4910
  locusId: uuid1,
4040
4911
  selfId: uuid2,
4041
4912
  mediaId: uuid3,
4042
- host: {id: uuid4}
4913
+ host: {id: uuid4},
4043
4914
  });
4044
4915
  assert.calledOnce(meeting.locusInfo.initialSetup);
4045
4916
  assert.calledWith(meeting.locusInfo.initialSetup, {
@@ -4048,7 +4919,7 @@ describe('plugin-meetings', () => {
4048
4919
  locusId: uuid1,
4049
4920
  selfId: uuid2,
4050
4921
  mediaId: uuid3,
4051
- host: {id: uuid4}
4922
+ host: {id: uuid4},
4052
4923
  });
4053
4924
  assert.equal(meeting.mediaConnections, test1);
4054
4925
  assert.equal(meeting.locusUrl, url1);
@@ -4097,7 +4968,7 @@ describe('plugin-meetings', () => {
4097
4968
  });
4098
4969
  it('should send the whiteboard share', async () => {
4099
4970
  const whiteboardShare = meeting.startWhiteboardShare({
4100
- channelUrl: url2
4971
+ channelUrl: url2,
4101
4972
  });
4102
4973
 
4103
4974
  assert.exists(whiteboardShare.then);
@@ -4137,18 +5008,35 @@ describe('plugin-meetings', () => {
4137
5008
  const USER_IDS = {
4138
5009
  ME: '9528d952-e4de-46cf-8157-fd4823b98377',
4139
5010
  REMOTE_A: '5be7e7b0-b304-48da-8083-83bd72b5300d',
4140
- REMOTE_B: 'd4d102a1-17ce-4e17-9b08-bded3de467e4'
5011
+ REMOTE_B: 'd4d102a1-17ce-4e17-9b08-bded3de467e4',
4141
5012
  };
4142
5013
 
4143
5014
  const RESOURCE_URLS = {
4144
- WHITEBOARD_A: 'https://board-a.wbx2.com/board/api/v1/channels/49cfb550-5517-11eb-a2af-1b9e4bc3da13',
4145
- WHITEBOARD_B: 'https://board-a.wbx2.com/board/api/v1/channels/977a7330-54f4-11eb-b1ef-91f5eefc7bf3'
5015
+ WHITEBOARD_A:
5016
+ 'https://board-a.wbx2.com/board/api/v1/channels/49cfb550-5517-11eb-a2af-1b9e4bc3da13',
5017
+ WHITEBOARD_B:
5018
+ 'https://board-a.wbx2.com/board/api/v1/channels/977a7330-54f4-11eb-b1ef-91f5eefc7bf3',
4146
5019
  };
4147
5020
 
4148
- const generateContent = (beneficiaryId = null, disposition = null) => ({beneficiaryId, disposition});
4149
- const generateWhiteboard = (beneficiaryId = null, disposition = null, resourceUrl = null) => ({beneficiaryId, disposition, resourceUrl});
4150
-
4151
- const generateData = (payload, isGranting, isContent, beneficiaryId, resourceUrl, isAccepting, otherBeneficiaryId) => {
5021
+ const generateContent = (beneficiaryId = null, disposition = null) => ({
5022
+ beneficiaryId,
5023
+ disposition,
5024
+ });
5025
+ const generateWhiteboard = (
5026
+ beneficiaryId = null,
5027
+ disposition = null,
5028
+ resourceUrl = null
5029
+ ) => ({beneficiaryId, disposition, resourceUrl});
5030
+
5031
+ const generateData = (
5032
+ payload,
5033
+ isGranting,
5034
+ isContent,
5035
+ beneficiaryId,
5036
+ resourceUrl,
5037
+ isAccepting,
5038
+ otherBeneficiaryId
5039
+ ) => {
4152
5040
  const newPayload = cloneDeep(payload);
4153
5041
 
4154
5042
  newPayload.previous = cloneDeep(payload.current);
@@ -4159,15 +5047,15 @@ describe('plugin-meetings', () => {
4159
5047
  eventName: EVENT_TRIGGERS.MEMBERS_CONTENT_UPDATE,
4160
5048
  eventPayload: {
4161
5049
  activeSharingId: null,
4162
- endedSharingId: null
4163
- }
4164
- }
5050
+ endedSharingId: null,
5051
+ },
5052
+ },
4165
5053
  };
4166
5054
 
4167
5055
  let shareStatus = null;
4168
5056
  const activeSharingId = {
4169
5057
  whiteboard: null,
4170
- content: null
5058
+ content: null,
4171
5059
  };
4172
5060
 
4173
5061
  if (isGranting) {
@@ -4177,68 +5065,72 @@ describe('plugin-meetings', () => {
4177
5065
 
4178
5066
  if (isEqual(newPayload.current, newPayload.previous)) {
4179
5067
  eventTrigger.member = null;
4180
- }
4181
- else {
5068
+ } else {
4182
5069
  if (newPayload.current.whiteboard.beneficiaryId) {
4183
5070
  if (newPayload.current.whiteboard.disposition === FLOOR_ACTION.GRANTED) {
4184
5071
  newPayload.current.whiteboard.disposition = FLOOR_ACTION.RELEASED;
4185
5072
  eventTrigger.share.push({
4186
5073
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD,
4187
- functionName: 'stopWhiteboardShare'
5074
+ functionName: 'stopWhiteboardShare',
4188
5075
  });
4189
- eventTrigger.member.eventPayload.endedSharingId = newPayload.current.whiteboard.beneficiaryId;
5076
+ eventTrigger.member.eventPayload.endedSharingId =
5077
+ newPayload.current.whiteboard.beneficiaryId;
4190
5078
  }
4191
5079
  }
4192
5080
 
4193
5081
  if (newPayload.previous.content.beneficiaryId) {
4194
- if (newPayload.previous.content.beneficiaryId !== newPayload.current.content.beneficiaryId) {
5082
+ if (
5083
+ newPayload.previous.content.beneficiaryId !==
5084
+ newPayload.current.content.beneficiaryId
5085
+ ) {
4195
5086
  if (newPayload.previous.content.beneficiaryId === USER_IDS.ME) {
4196
5087
  eventTrigger.share.push({
4197
5088
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
4198
- functionName: 'stopFloorRequest'
5089
+ functionName: 'localShare',
4199
5090
  });
4200
- }
4201
- else if (newPayload.current.content.beneficiaryId === USER_IDS.ME) {
5091
+ } else if (newPayload.current.content.beneficiaryId === USER_IDS.ME) {
4202
5092
  eventTrigger.share.push({
4203
5093
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_REMOTE,
4204
- functionName: 'remoteShare'
5094
+ functionName: 'remoteShare',
4205
5095
  });
4206
5096
  }
4207
- eventTrigger.member.eventPayload.endedSharingId = newPayload.previous.content.beneficiaryId;
5097
+ eventTrigger.member.eventPayload.endedSharingId =
5098
+ newPayload.previous.content.beneficiaryId;
4208
5099
  }
4209
5100
  }
4210
5101
 
4211
5102
  if (isAccepting) {
4212
5103
  eventTrigger.share.push({
4213
5104
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD,
4214
- functionName: 'stopWhiteboardShare'
5105
+ functionName: 'stopWhiteboardShare',
4215
5106
  });
4216
5107
  }
4217
5108
 
4218
5109
  if (beneficiaryId === USER_IDS.ME) {
4219
5110
  eventTrigger.share.push({
4220
5111
  eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_LOCAL,
4221
- functionName: 'share'
5112
+ functionName: 'share',
4222
5113
  });
4223
- }
4224
- else {
5114
+ } else {
4225
5115
  eventTrigger.share.push({
4226
5116
  eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
4227
5117
  functionName: 'remoteShare',
4228
- eventPayload: {memberId: beneficiaryId}
5118
+ eventPayload: {memberId: beneficiaryId},
4229
5119
  });
4230
5120
  }
4231
5121
  }
4232
5122
 
4233
5123
  if (beneficiaryId === USER_IDS.ME) {
4234
5124
  shareStatus = SHARE_STATUS.LOCAL_SHARE_ACTIVE;
4235
- }
4236
- else {
5125
+ } else {
4237
5126
  shareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
4238
5127
  }
4239
- }
4240
- else {
4241
- newPayload.current.whiteboard = generateWhiteboard(beneficiaryId, FLOOR_ACTION.GRANTED, resourceUrl);
5128
+ } else {
5129
+ newPayload.current.whiteboard = generateWhiteboard(
5130
+ beneficiaryId,
5131
+ FLOOR_ACTION.GRANTED,
5132
+ resourceUrl
5133
+ );
4242
5134
 
4243
5135
  if (newPayload.current.content.beneficiaryId) {
4244
5136
  if (newPayload.current.content.disposition === FLOOR_ACTION.GRANTED) {
@@ -4246,41 +5138,48 @@ describe('plugin-meetings', () => {
4246
5138
  if (newPayload.current.content.beneficiaryId === USER_IDS.ME) {
4247
5139
  eventTrigger.share.push({
4248
5140
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
4249
- functionName: 'stopFloorRequest'
5141
+ functionName: 'localShare',
4250
5142
  });
4251
- }
4252
- else {
5143
+ } else {
4253
5144
  eventTrigger.share.push({
4254
5145
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_REMOTE,
4255
- functionName: 'remoteShare'
5146
+ functionName: 'remoteShare',
4256
5147
  });
4257
5148
  }
4258
5149
 
4259
- eventTrigger.member.eventPayload.endedSharingId = newPayload.current.content.beneficiaryId;
5150
+ eventTrigger.member.eventPayload.endedSharingId =
5151
+ newPayload.current.content.beneficiaryId;
4260
5152
  }
4261
5153
  }
4262
5154
 
4263
5155
  if (newPayload.previous.content.beneficiaryId) {
4264
- if (newPayload.previous.content.beneficiaryId !== newPayload.current.content.beneficiaryId) {
5156
+ if (
5157
+ newPayload.previous.content.beneficiaryId !==
5158
+ newPayload.current.content.beneficiaryId
5159
+ ) {
4265
5160
  if (newPayload.previous.content.beneficiaryId === USER_IDS.ME) {
4266
5161
  eventTrigger.share.push({
4267
5162
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
4268
- functionName: 'stopFloorRequest'
5163
+ functionName: 'localShare',
4269
5164
  });
4270
- }
4271
- else if (newPayload.current.content.beneficiaryId === USER_IDS.ME) {
5165
+ } else if (newPayload.current.content.beneficiaryId === USER_IDS.ME) {
4272
5166
  eventTrigger.share.push({
4273
5167
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_REMOTE,
4274
- functionName: 'remoteShare'
5168
+ functionName: 'remoteShare',
4275
5169
  });
4276
5170
  }
4277
- eventTrigger.member.eventPayload.endedSharingId = newPayload.previous.content.beneficiaryId;
5171
+ eventTrigger.member.eventPayload.endedSharingId =
5172
+ newPayload.previous.content.beneficiaryId;
4278
5173
  }
4279
5174
  }
4280
5175
 
4281
5176
  if (newPayload.previous.whiteboard.beneficiaryId) {
4282
- if (newPayload.previous.whiteboard.beneficiaryId !== newPayload.current.whiteboard.beneficiaryId) {
4283
- eventTrigger.member.eventPayload.endedSharingId = newPayload.previous.whiteboard.beneficiaryId;
5177
+ if (
5178
+ newPayload.previous.whiteboard.beneficiaryId !==
5179
+ newPayload.current.whiteboard.beneficiaryId
5180
+ ) {
5181
+ eventTrigger.member.eventPayload.endedSharingId =
5182
+ newPayload.previous.whiteboard.beneficiaryId;
4284
5183
  }
4285
5184
  }
4286
5185
 
@@ -4289,7 +5188,7 @@ describe('plugin-meetings', () => {
4289
5188
  eventTrigger.share.push({
4290
5189
  eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
4291
5190
  functionName: 'startWhiteboardShare',
4292
- eventPayload: {resourceUrl, memberId: beneficiaryId}
5191
+ eventPayload: {resourceUrl, memberId: beneficiaryId},
4293
5192
  });
4294
5193
 
4295
5194
  shareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
@@ -4298,8 +5197,7 @@ describe('plugin-meetings', () => {
4298
5197
  if (eventTrigger.member) {
4299
5198
  eventTrigger.member.eventPayload.activeSharingId = beneficiaryId;
4300
5199
  }
4301
- }
4302
- else {
5200
+ } else {
4303
5201
  eventTrigger.member.eventPayload.endedSharingId = beneficiaryId;
4304
5202
 
4305
5203
  if (isContent) {
@@ -4308,19 +5206,17 @@ describe('plugin-meetings', () => {
4308
5206
  if (beneficiaryId === USER_IDS.ME) {
4309
5207
  eventTrigger.share.push({
4310
5208
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_LOCAL,
4311
- functionName: 'stopFloorRequest'
5209
+ functionName: 'localShare',
4312
5210
  });
4313
- }
4314
- else {
5211
+ } else {
4315
5212
  eventTrigger.share.push({
4316
5213
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_REMOTE,
4317
- functionName: 'remoteShare'
5214
+ functionName: 'remoteShare',
4318
5215
  });
4319
5216
  }
4320
5217
 
4321
5218
  shareStatus = SHARE_STATUS.NO_SHARE;
4322
- }
4323
- else {
5219
+ } else {
4324
5220
  newPayload.current.whiteboard.disposition = FLOOR_ACTION.RELEASED;
4325
5221
 
4326
5222
  if (isAccepting) {
@@ -4330,15 +5226,14 @@ describe('plugin-meetings', () => {
4330
5226
  eventTrigger.share.push({
4331
5227
  eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
4332
5228
  functionName: 'startWhiteboardShare',
4333
- eventPayload: {resourceUrl, memberId: beneficiaryId}
5229
+ eventPayload: {resourceUrl, memberId: beneficiaryId},
4334
5230
  });
4335
5231
 
4336
5232
  shareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
4337
- }
4338
- else {
5233
+ } else {
4339
5234
  eventTrigger.share.push({
4340
5235
  eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD,
4341
- functionName: 'stopWhiteboardShare'
5236
+ functionName: 'stopWhiteboardShare',
4342
5237
  });
4343
5238
 
4344
5239
  shareStatus = SHARE_STATUS.NO_SHARE;
@@ -4347,22 +5242,24 @@ describe('plugin-meetings', () => {
4347
5242
  }
4348
5243
 
4349
5244
  return {
4350
- payload: newPayload, eventTrigger, shareStatus, activeSharingId
5245
+ payload: newPayload,
5246
+ eventTrigger,
5247
+ shareStatus,
5248
+ activeSharingId,
4351
5249
  };
4352
5250
  };
4353
5251
 
4354
5252
  const blankPayload = {
4355
5253
  previous: {
4356
5254
  content: generateContent(),
4357
- whiteboard: generateWhiteboard()
5255
+ whiteboard: generateWhiteboard(),
4358
5256
  },
4359
5257
  current: {
4360
5258
  content: generateContent(),
4361
- whiteboard: generateWhiteboard()
4362
- }
5259
+ whiteboard: generateWhiteboard(),
5260
+ },
4363
5261
  };
4364
5262
 
4365
-
4366
5263
  const payloadTestHelper = (data) => {
4367
5264
  assert.equal(meeting.shareStatus, SHARE_STATUS.NO_SHARE);
4368
5265
 
@@ -4370,23 +5267,33 @@ describe('plugin-meetings', () => {
4370
5267
  let callCounter = 1;
4371
5268
 
4372
5269
  data.forEach((d, index) => {
4373
- meeting.locusInfo.emit({function: 'test', file: 'test'}, EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES, d.payload);
5270
+ meeting.locusInfo.emit(
5271
+ {function: 'test', file: 'test'},
5272
+ EVENTS.LOCUS_INFO_UPDATE_MEDIA_SHARES,
5273
+ d.payload
5274
+ );
4374
5275
 
4375
5276
  assert.equal(meeting.shareStatus, data[index].shareStatus);
4376
5277
 
4377
- callCounter += data[index].eventTrigger.share.length + (data[index].eventTrigger.member ? 1 : 0);
5278
+ callCounter +=
5279
+ data[index].eventTrigger.share.length + (data[index].eventTrigger.member ? 1 : 0);
4378
5280
 
4379
5281
  assert.callCount(TriggerProxy.trigger, callCounter);
4380
5282
 
4381
- assert.equal(meeting.members.mediaShareWhiteboardId, data[index].activeSharingId.whiteboard);
4382
- assert.equal(meeting.members.mediaShareContentId, data[index].activeSharingId.content);
5283
+ assert.equal(
5284
+ meeting.members.mediaShareWhiteboardId,
5285
+ data[index].activeSharingId.whiteboard
5286
+ );
5287
+ assert.equal(
5288
+ meeting.members.mediaShareContentId,
5289
+ data[index].activeSharingId.content
5290
+ );
4383
5291
  });
4384
5292
 
4385
5293
  assert.callCount(TriggerProxy.trigger, callCounter);
4386
5294
 
4387
5295
  // Start with 1 to ignore members:update trigger
4388
5296
 
4389
-
4390
5297
  let i = 1;
4391
5298
  let offset = 2;
4392
5299
 
@@ -4398,21 +5305,24 @@ describe('plugin-meetings', () => {
4398
5305
  for (let idx = 0; idx < share.length; idx += 1) {
4399
5306
  const shareCallArgs = TriggerProxy.trigger.getCall(i + idx).args;
4400
5307
  const {functionName, eventName, eventPayload} = share[idx];
4401
- const fileName = functionName === 'remoteShare' ? 'meetings/index' : 'meeting/index';
5308
+ const fileName =
5309
+ functionName === 'remoteShare' ? 'meetings/index' : 'meeting/index';
4402
5310
 
4403
5311
  assert.deepEqual(shareCallArgs[1], {
4404
5312
  file: fileName,
4405
- function: functionName
5313
+ function: functionName,
4406
5314
  });
4407
5315
 
4408
-
4409
5316
  assert.equal(shareCallArgs[2], eventName);
4410
5317
 
4411
5318
  if (functionName === 'startWhiteboardShare') {
4412
5319
  assert.deepEqual(shareCallArgs[3], eventPayload);
4413
5320
  }
4414
5321
 
4415
- if (functionName === 'remoteShare' && eventName === EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE) {
5322
+ if (
5323
+ functionName === 'remoteShare' &&
5324
+ eventName === EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE
5325
+ ) {
4416
5326
  assert.deepEqual(shareCallArgs[3], eventPayload);
4417
5327
  }
4418
5328
  }
@@ -4423,7 +5333,7 @@ describe('plugin-meetings', () => {
4423
5333
 
4424
5334
  assert.deepEqual(memberCallArgs[1], {
4425
5335
  file: 'members',
4426
- function: 'locusMediaSharesUpdate'
5336
+ function: 'locusMediaSharesUpdate',
4427
5337
  });
4428
5338
  assert.equal(memberCallArgs[2], member.eventName);
4429
5339
 
@@ -4437,9 +5347,8 @@ describe('plugin-meetings', () => {
4437
5347
 
4438
5348
  if (share.length + 1 > offset) {
4439
5349
  offset = (offset + share.length + 1) / 2;
4440
- }
4441
- else if (share.length + 1 < offset) {
4442
- offset = (share.length + 1) + 0.5;
5350
+ } else if (share.length + 1 < offset) {
5351
+ offset = share.length + 1 + 0.5;
4443
5352
  }
4444
5353
  }
4445
5354
  };
@@ -4450,40 +5359,100 @@ describe('plugin-meetings', () => {
4450
5359
 
4451
5360
  describe('Whiteboard A --> Whiteboard B', () => {
4452
5361
  it('Scenario #1: you share both whiteboards', () => {
4453
- const data1 = generateData(blankPayload, true, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_A);
4454
- const data2 = generateData(data1.payload, true, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_B);
5362
+ const data1 = generateData(
5363
+ blankPayload,
5364
+ true,
5365
+ false,
5366
+ USER_IDS.ME,
5367
+ RESOURCE_URLS.WHITEBOARD_A
5368
+ );
5369
+ const data2 = generateData(
5370
+ data1.payload,
5371
+ true,
5372
+ false,
5373
+ USER_IDS.ME,
5374
+ RESOURCE_URLS.WHITEBOARD_B
5375
+ );
4455
5376
  const data3 = generateData(data2.payload, false, false, USER_IDS.ME);
4456
5377
 
4457
5378
  payloadTestHelper([data1, data2, data3]);
4458
5379
  });
4459
5380
 
4460
5381
  it('Scenario #2: you share whiteboard A and remote person A shares whiteboard B', () => {
4461
- const data1 = generateData(blankPayload, true, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_A);
4462
- const data2 = generateData(data1.payload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_B);
5382
+ const data1 = generateData(
5383
+ blankPayload,
5384
+ true,
5385
+ false,
5386
+ USER_IDS.ME,
5387
+ RESOURCE_URLS.WHITEBOARD_A
5388
+ );
5389
+ const data2 = generateData(
5390
+ data1.payload,
5391
+ true,
5392
+ false,
5393
+ USER_IDS.REMOTE_A,
5394
+ RESOURCE_URLS.WHITEBOARD_B
5395
+ );
4463
5396
  const data3 = generateData(data2.payload, false, false, USER_IDS.REMOTE_A);
4464
5397
 
4465
5398
  payloadTestHelper([data1, data2, data3]);
4466
5399
  });
4467
5400
 
4468
5401
  it('Scenario #3: remote person A shares whiteboard A and you share whiteboard B', () => {
4469
- const data1 = generateData(blankPayload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A);
4470
- const data2 = generateData(data1.payload, true, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_B);
5402
+ const data1 = generateData(
5403
+ blankPayload,
5404
+ true,
5405
+ false,
5406
+ USER_IDS.REMOTE_A,
5407
+ RESOURCE_URLS.WHITEBOARD_A
5408
+ );
5409
+ const data2 = generateData(
5410
+ data1.payload,
5411
+ true,
5412
+ false,
5413
+ USER_IDS.ME,
5414
+ RESOURCE_URLS.WHITEBOARD_B
5415
+ );
4471
5416
  const data3 = generateData(data2.payload, false, false, USER_IDS.ME);
4472
5417
 
4473
5418
  payloadTestHelper([data1, data2, data3]);
4474
5419
  });
4475
5420
 
4476
5421
  it('Scenario #4: remote person A shares both whiteboards', () => {
4477
- const data1 = generateData(blankPayload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A);
4478
- const data2 = generateData(data1.payload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_B);
5422
+ const data1 = generateData(
5423
+ blankPayload,
5424
+ true,
5425
+ false,
5426
+ USER_IDS.REMOTE_A,
5427
+ RESOURCE_URLS.WHITEBOARD_A
5428
+ );
5429
+ const data2 = generateData(
5430
+ data1.payload,
5431
+ true,
5432
+ false,
5433
+ USER_IDS.REMOTE_A,
5434
+ RESOURCE_URLS.WHITEBOARD_B
5435
+ );
4479
5436
  const data3 = generateData(data2.payload, false, false, USER_IDS.REMOTE_A);
4480
5437
 
4481
5438
  payloadTestHelper([data1, data2, data3]);
4482
5439
  });
4483
5440
 
4484
5441
  it('Scenario #5: remote person A shares whiteboard A and remote person B shares whiteboard B', () => {
4485
- const data1 = generateData(blankPayload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A);
4486
- const data2 = generateData(data1.payload, true, false, USER_IDS.REMOTE_B, RESOURCE_URLS.WHITEBOARD_B);
5442
+ const data1 = generateData(
5443
+ blankPayload,
5444
+ true,
5445
+ false,
5446
+ USER_IDS.REMOTE_A,
5447
+ RESOURCE_URLS.WHITEBOARD_A
5448
+ );
5449
+ const data2 = generateData(
5450
+ data1.payload,
5451
+ true,
5452
+ false,
5453
+ USER_IDS.REMOTE_B,
5454
+ RESOURCE_URLS.WHITEBOARD_B
5455
+ );
4487
5456
  const data3 = generateData(data2.payload, false, false, USER_IDS.REMOTE_B);
4488
5457
 
4489
5458
  payloadTestHelper([data1, data2, data3]);
@@ -4492,45 +5461,155 @@ describe('plugin-meetings', () => {
4492
5461
 
4493
5462
  describe('Whiteboard A --> Desktop', () => {
4494
5463
  it('Scenario #1: you share whiteboard and then share desktop', () => {
4495
- const data1 = generateData(blankPayload, true, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_A);
4496
- const data2 = generateData(data1.payload, false, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_A, true, USER_IDS.ME);
4497
- const data3 = generateData(data2.payload, true, true, USER_IDS.ME, undefined, true, USER_IDS.ME);
5464
+ const data1 = generateData(
5465
+ blankPayload,
5466
+ true,
5467
+ false,
5468
+ USER_IDS.ME,
5469
+ RESOURCE_URLS.WHITEBOARD_A
5470
+ );
5471
+ const data2 = generateData(
5472
+ data1.payload,
5473
+ false,
5474
+ false,
5475
+ USER_IDS.ME,
5476
+ RESOURCE_URLS.WHITEBOARD_A,
5477
+ true,
5478
+ USER_IDS.ME
5479
+ );
5480
+ const data3 = generateData(
5481
+ data2.payload,
5482
+ true,
5483
+ true,
5484
+ USER_IDS.ME,
5485
+ undefined,
5486
+ true,
5487
+ USER_IDS.ME
5488
+ );
4498
5489
  const data4 = generateData(data3.payload, false, true, USER_IDS.ME);
4499
5490
 
4500
5491
  payloadTestHelper([data1, data2, data3, data4]);
4501
5492
  });
4502
5493
 
4503
5494
  it('Scenario #2: you share whiteboard A and remote person A shares desktop', () => {
4504
- const data1 = generateData(blankPayload, true, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_A);
4505
- const data2 = generateData(data1.payload, false, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_A, true, USER_IDS.REMOTE_A);
4506
- const data3 = generateData(data2.payload, true, true, USER_IDS.REMOTE_A, undefined, true, USER_IDS.ME);
5495
+ const data1 = generateData(
5496
+ blankPayload,
5497
+ true,
5498
+ false,
5499
+ USER_IDS.ME,
5500
+ RESOURCE_URLS.WHITEBOARD_A
5501
+ );
5502
+ const data2 = generateData(
5503
+ data1.payload,
5504
+ false,
5505
+ false,
5506
+ USER_IDS.ME,
5507
+ RESOURCE_URLS.WHITEBOARD_A,
5508
+ true,
5509
+ USER_IDS.REMOTE_A
5510
+ );
5511
+ const data3 = generateData(
5512
+ data2.payload,
5513
+ true,
5514
+ true,
5515
+ USER_IDS.REMOTE_A,
5516
+ undefined,
5517
+ true,
5518
+ USER_IDS.ME
5519
+ );
4507
5520
  const data4 = generateData(data3.payload, false, true, USER_IDS.REMOTE_A);
4508
5521
 
4509
5522
  payloadTestHelper([data1, data2, data3, data4]);
4510
5523
  });
4511
5524
 
4512
5525
  it('Scenario #3: remote person A shares whiteboard and you share desktop', () => {
4513
- const data1 = generateData(blankPayload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A);
4514
- const data2 = generateData(data1.payload, false, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A, true, USER_IDS.ME);
4515
- const data3 = generateData(data2.payload, true, true, USER_IDS.ME, undefined, true, USER_IDS.REMOTE_A);
5526
+ const data1 = generateData(
5527
+ blankPayload,
5528
+ true,
5529
+ false,
5530
+ USER_IDS.REMOTE_A,
5531
+ RESOURCE_URLS.WHITEBOARD_A
5532
+ );
5533
+ const data2 = generateData(
5534
+ data1.payload,
5535
+ false,
5536
+ false,
5537
+ USER_IDS.REMOTE_A,
5538
+ RESOURCE_URLS.WHITEBOARD_A,
5539
+ true,
5540
+ USER_IDS.ME
5541
+ );
5542
+ const data3 = generateData(
5543
+ data2.payload,
5544
+ true,
5545
+ true,
5546
+ USER_IDS.ME,
5547
+ undefined,
5548
+ true,
5549
+ USER_IDS.REMOTE_A
5550
+ );
4516
5551
  const data4 = generateData(data3.payload, false, true, USER_IDS.ME);
4517
5552
 
4518
5553
  payloadTestHelper([data1, data2, data3, data4]);
4519
5554
  });
4520
5555
 
4521
5556
  it('Scenario #4: remote person A shares whiteboard and then shares desktop', () => {
4522
- const data1 = generateData(blankPayload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A);
4523
- const data2 = generateData(data1.payload, false, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A, true, USER_IDS.REMOTE_A);
4524
- const data3 = generateData(data2.payload, true, true, USER_IDS.REMOTE_A, undefined, true, USER_IDS.REMOTE_A);
5557
+ const data1 = generateData(
5558
+ blankPayload,
5559
+ true,
5560
+ false,
5561
+ USER_IDS.REMOTE_A,
5562
+ RESOURCE_URLS.WHITEBOARD_A
5563
+ );
5564
+ const data2 = generateData(
5565
+ data1.payload,
5566
+ false,
5567
+ false,
5568
+ USER_IDS.REMOTE_A,
5569
+ RESOURCE_URLS.WHITEBOARD_A,
5570
+ true,
5571
+ USER_IDS.REMOTE_A
5572
+ );
5573
+ const data3 = generateData(
5574
+ data2.payload,
5575
+ true,
5576
+ true,
5577
+ USER_IDS.REMOTE_A,
5578
+ undefined,
5579
+ true,
5580
+ USER_IDS.REMOTE_A
5581
+ );
4525
5582
  const data4 = generateData(data3.payload, false, true, USER_IDS.REMOTE_A);
4526
5583
 
4527
5584
  payloadTestHelper([data1, data2, data3, data4]);
4528
5585
  });
4529
5586
 
4530
5587
  it('Scenario #5: remote person A shares whiteboard and remote person B shares desktop', () => {
4531
- const data1 = generateData(blankPayload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A);
4532
- const data2 = generateData(data1.payload, false, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A, true, USER_IDS.REMOTE_B);
4533
- const data3 = generateData(data2.payload, true, true, USER_IDS.REMOTE_B, undefined, true, USER_IDS.REMOTE_A);
5588
+ const data1 = generateData(
5589
+ blankPayload,
5590
+ true,
5591
+ false,
5592
+ USER_IDS.REMOTE_A,
5593
+ RESOURCE_URLS.WHITEBOARD_A
5594
+ );
5595
+ const data2 = generateData(
5596
+ data1.payload,
5597
+ false,
5598
+ false,
5599
+ USER_IDS.REMOTE_A,
5600
+ RESOURCE_URLS.WHITEBOARD_A,
5601
+ true,
5602
+ USER_IDS.REMOTE_B
5603
+ );
5604
+ const data3 = generateData(
5605
+ data2.payload,
5606
+ true,
5607
+ true,
5608
+ USER_IDS.REMOTE_B,
5609
+ undefined,
5610
+ true,
5611
+ USER_IDS.REMOTE_A
5612
+ );
4534
5613
  const data4 = generateData(data3.payload, false, true, USER_IDS.REMOTE_B);
4535
5614
 
4536
5615
  payloadTestHelper([data1, data2, data3, data4]);
@@ -4540,7 +5619,13 @@ describe('plugin-meetings', () => {
4540
5619
  describe('Desktop --> Whiteboard A', () => {
4541
5620
  it('Scenario #1: you share desktop and then share whiteboard', () => {
4542
5621
  const data1 = generateData(blankPayload, true, true, USER_IDS.ME);
4543
- const data2 = generateData(data1.payload, true, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_A);
5622
+ const data2 = generateData(
5623
+ data1.payload,
5624
+ true,
5625
+ false,
5626
+ USER_IDS.ME,
5627
+ RESOURCE_URLS.WHITEBOARD_A
5628
+ );
4544
5629
  const data3 = generateData(data2.payload, false, false, USER_IDS.ME);
4545
5630
 
4546
5631
  payloadTestHelper([data1, data2, data3]);
@@ -4548,7 +5633,13 @@ describe('plugin-meetings', () => {
4548
5633
 
4549
5634
  it('Scenario #2: you share desktop and remote person A shares whiteboard', () => {
4550
5635
  const data1 = generateData(blankPayload, true, true, USER_IDS.ME);
4551
- const data2 = generateData(data1.payload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A);
5636
+ const data2 = generateData(
5637
+ data1.payload,
5638
+ true,
5639
+ false,
5640
+ USER_IDS.REMOTE_A,
5641
+ RESOURCE_URLS.WHITEBOARD_A
5642
+ );
4552
5643
  const data3 = generateData(data2.payload, false, false, USER_IDS.REMOTE_A);
4553
5644
 
4554
5645
  payloadTestHelper([data1, data2, data3]);
@@ -4556,7 +5647,13 @@ describe('plugin-meetings', () => {
4556
5647
 
4557
5648
  it('Scenario #3: remote person A shares desktop and you share whiteboard', () => {
4558
5649
  const data1 = generateData(blankPayload, true, true, USER_IDS.REMOTE_A);
4559
- const data2 = generateData(data1.payload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A);
5650
+ const data2 = generateData(
5651
+ data1.payload,
5652
+ true,
5653
+ false,
5654
+ USER_IDS.REMOTE_A,
5655
+ RESOURCE_URLS.WHITEBOARD_A
5656
+ );
4560
5657
  const data3 = generateData(data2.payload, false, false, USER_IDS.REMOTE_A);
4561
5658
 
4562
5659
  payloadTestHelper([data1, data2, data3]);
@@ -4564,7 +5661,13 @@ describe('plugin-meetings', () => {
4564
5661
 
4565
5662
  it('Scenario #4: remote person A shares desktop and then shares whiteboard', () => {
4566
5663
  const data1 = generateData(blankPayload, true, true, USER_IDS.REMOTE_A);
4567
- const data2 = generateData(data1.payload, true, false, USER_IDS.ME, RESOURCE_URLS.WHITEBOARD_A);
5664
+ const data2 = generateData(
5665
+ data1.payload,
5666
+ true,
5667
+ false,
5668
+ USER_IDS.ME,
5669
+ RESOURCE_URLS.WHITEBOARD_A
5670
+ );
4568
5671
  const data3 = generateData(data2.payload, false, false, USER_IDS.ME);
4569
5672
 
4570
5673
  payloadTestHelper([data1, data2, data3]);
@@ -4572,7 +5675,13 @@ describe('plugin-meetings', () => {
4572
5675
 
4573
5676
  it('Scenario #5: remote person A shares desktop and remote person B shares whiteboard', () => {
4574
5677
  const data1 = generateData(blankPayload, true, true, USER_IDS.REMOTE_A);
4575
- const data2 = generateData(data1.payload, true, false, USER_IDS.REMOTE_A, RESOURCE_URLS.WHITEBOARD_A);
5678
+ const data2 = generateData(
5679
+ data1.payload,
5680
+ true,
5681
+ false,
5682
+ USER_IDS.REMOTE_A,
5683
+ RESOURCE_URLS.WHITEBOARD_A
5684
+ );
4576
5685
  const data3 = generateData(data2.payload, false, false, USER_IDS.REMOTE_A);
4577
5686
 
4578
5687
  payloadTestHelper([data1, data2, data3]);
@@ -4648,17 +5757,21 @@ describe('plugin-meetings', () => {
4648
5757
  assert.isNull(meeting.keepAliveTimerId);
4649
5758
  meeting.joinedWith = {
4650
5759
  keepAliveUrl: defaultKeepAliveUrl,
4651
- keepAliveSecs: defaultKeepAliveSecs
5760
+ keepAliveSecs: defaultKeepAliveSecs,
4652
5761
  };
4653
5762
  meeting.startKeepAlive();
4654
5763
  assert.isNumber(meeting.keepAliveTimerId.id);
4655
5764
  await testUtils.flushPromises();
4656
5765
  assert.notCalled(meeting.meetingRequest.keepAlive);
4657
5766
  await progressTime(defaultExpectedInterval);
4658
- assert.calledOnceWithExactly(meeting.meetingRequest.keepAlive, {keepAliveUrl: defaultKeepAliveUrl});
5767
+ assert.calledOnceWithExactly(meeting.meetingRequest.keepAlive, {
5768
+ keepAliveUrl: defaultKeepAliveUrl,
5769
+ });
4659
5770
  await progressTime(defaultExpectedInterval);
4660
5771
  assert.calledTwice(meeting.meetingRequest.keepAlive);
4661
- assert.alwaysCalledWithExactly(meeting.meetingRequest.keepAlive, {keepAliveUrl: defaultKeepAliveUrl});
5772
+ assert.alwaysCalledWithExactly(meeting.meetingRequest.keepAlive, {
5773
+ keepAliveUrl: defaultKeepAliveUrl,
5774
+ });
4662
5775
  });
4663
5776
  it('startKeepAlive handles existing keepAliveTimerId', async () => {
4664
5777
  meeting.meetingRequest.keepAlive = sinon.stub().returns(Promise.resolve());
@@ -4667,7 +5780,7 @@ describe('plugin-meetings', () => {
4667
5780
  meeting.keepAliveTimerId = 7;
4668
5781
  meeting.joinedWith = {
4669
5782
  keepAliveUrl: defaultKeepAliveUrl,
4670
- keepAliveSecs: defaultKeepAliveSecs
5783
+ keepAliveSecs: defaultKeepAliveSecs,
4671
5784
  };
4672
5785
  meeting.startKeepAlive();
4673
5786
  assert.equal(meeting.keepAliveTimerId, 7);
@@ -4680,7 +5793,7 @@ describe('plugin-meetings', () => {
4680
5793
 
4681
5794
  assert.isNull(meeting.keepAliveTimerId);
4682
5795
  meeting.joinedWith = {
4683
- keepAliveSecs: defaultKeepAliveSecs
5796
+ keepAliveSecs: defaultKeepAliveSecs,
4684
5797
  };
4685
5798
  meeting.startKeepAlive();
4686
5799
  assert.isNull(meeting.keepAliveTimerId);
@@ -4693,7 +5806,7 @@ describe('plugin-meetings', () => {
4693
5806
 
4694
5807
  assert.isNull(meeting.keepAliveTimerId);
4695
5808
  meeting.joinedWith = {
4696
- keepAliveUrl: defaultKeepAliveUrl
5809
+ keepAliveUrl: defaultKeepAliveUrl,
4697
5810
  };
4698
5811
  meeting.startKeepAlive();
4699
5812
  assert.isNull(meeting.keepAliveTimerId);
@@ -4707,7 +5820,7 @@ describe('plugin-meetings', () => {
4707
5820
  assert.isNull(meeting.keepAliveTimerId);
4708
5821
  meeting.joinedWith = {
4709
5822
  keepAliveUrl: defaultKeepAliveUrl,
4710
- keepAliveSecs: 1
5823
+ keepAliveSecs: 1,
4711
5824
  };
4712
5825
  meeting.startKeepAlive();
4713
5826
  assert.isNull(meeting.keepAliveTimerId);
@@ -4720,14 +5833,16 @@ describe('plugin-meetings', () => {
4720
5833
  assert.isNull(meeting.keepAliveTimerId);
4721
5834
  meeting.joinedWith = {
4722
5835
  keepAliveUrl: defaultKeepAliveUrl,
4723
- keepAliveSecs: defaultKeepAliveSecs
5836
+ keepAliveSecs: defaultKeepAliveSecs,
4724
5837
  };
4725
5838
  meeting.startKeepAlive();
4726
5839
  assert.isNumber(meeting.keepAliveTimerId.id);
4727
5840
  await testUtils.flushPromises();
4728
5841
  assert.notCalled(meeting.meetingRequest.keepAlive);
4729
5842
  await progressTime(defaultExpectedInterval);
4730
- assert.calledOnceWithExactly(meeting.meetingRequest.keepAlive, {keepAliveUrl: defaultKeepAliveUrl});
5843
+ assert.calledOnceWithExactly(meeting.meetingRequest.keepAlive, {
5844
+ keepAliveUrl: defaultKeepAliveUrl,
5845
+ });
4731
5846
  assert.isNull(meeting.keepAliveTimerId);
4732
5847
  await progressTime(defaultExpectedInterval);
4733
5848
  assert.calledOnce(meeting.meetingRequest.keepAlive);
@@ -4757,12 +5872,14 @@ describe('plugin-meetings', () => {
4757
5872
  assert.isNull(meeting.keepAliveTimerId);
4758
5873
  meeting.joinedWith = {
4759
5874
  keepAliveUrl: defaultKeepAliveUrl,
4760
- keepAliveSecs: defaultKeepAliveSecs
5875
+ keepAliveSecs: defaultKeepAliveSecs,
4761
5876
  };
4762
5877
  meeting.startKeepAlive();
4763
5878
  assert.isNumber(meeting.keepAliveTimerId.id);
4764
5879
  await progressTime(defaultExpectedInterval);
4765
- assert.calledOnceWithExactly(meeting.meetingRequest.keepAlive, {keepAliveUrl: defaultKeepAliveUrl});
5880
+ assert.calledOnceWithExactly(meeting.meetingRequest.keepAlive, {
5881
+ keepAliveUrl: defaultKeepAliveUrl,
5882
+ });
4766
5883
 
4767
5884
  meeting.stopKeepAlive();
4768
5885
  assert.isNull(meeting.keepAliveTimerId);
@@ -4774,6 +5891,164 @@ describe('plugin-meetings', () => {
4774
5891
  meeting.stopKeepAlive();
4775
5892
  });
4776
5893
  });
5894
+
5895
+ describe('#sendReaction', () => {
5896
+ it('should have #sendReaction', () => {
5897
+ assert.exists(meeting.sendReaction);
5898
+ });
5899
+
5900
+ beforeEach(() => {
5901
+ meeting.meetingRequest.sendReaction = sinon.stub().returns(Promise.resolve());
5902
+ });
5903
+
5904
+ it('should send reaction with the right data and return a promise', async () => {
5905
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: 'Fake URL'}};
5906
+
5907
+ const reactionPromise = meeting.sendReaction('thumbs_down', 'light');
5908
+
5909
+ assert.exists(reactionPromise.then);
5910
+ await reactionPromise;
5911
+ assert.calledOnceWithExactly(meeting.meetingRequest.sendReaction, {
5912
+ reactionChannelUrl: 'Fake URL',
5913
+ reaction: {
5914
+ type: 'thumb_down',
5915
+ codepoints: '1F44E',
5916
+ shortcodes: ':thumbsdown:',
5917
+ tone: {
5918
+ type: 'light_skin_tone',
5919
+ codepoints: '1F3FB',
5920
+ shortcodes: ':skin-tone-2:',
5921
+ },
5922
+ },
5923
+ participantId: meeting.members.selfId,
5924
+ });
5925
+ });
5926
+
5927
+ it('should fail sending a reaction if data channel is undefined', async () => {
5928
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: undefined}};
5929
+
5930
+ await assert.isRejected(
5931
+ meeting.sendReaction('thumbs_down', 'light'),
5932
+ Error,
5933
+ 'Error sending reaction, service url not found.'
5934
+ );
5935
+
5936
+ assert.notCalled(meeting.meetingRequest.sendReaction);
5937
+ });
5938
+
5939
+ it('should fail sending a reaction if reactionType is invalid ', async () => {
5940
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: 'Fake URL'}};
5941
+
5942
+ await assert.isRejected(
5943
+ meeting.sendReaction('invalid_reaction', 'light'),
5944
+ Error,
5945
+ 'invalid_reaction is not a valid reaction.'
5946
+ );
5947
+
5948
+ assert.notCalled(meeting.meetingRequest.sendReaction);
5949
+ });
5950
+
5951
+ it('should send a reaction with default skin tone if provided skinToneType is invalid ', async () => {
5952
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: 'Fake URL'}};
5953
+
5954
+ const reactionPromise = meeting.sendReaction('thumbs_down', 'invalid_skin_tone');
5955
+
5956
+ assert.exists(reactionPromise.then);
5957
+ await reactionPromise;
5958
+ assert.calledOnceWithExactly(meeting.meetingRequest.sendReaction, {
5959
+ reactionChannelUrl: 'Fake URL',
5960
+ reaction: {
5961
+ type: 'thumb_down',
5962
+ codepoints: '1F44E',
5963
+ shortcodes: ':thumbsdown:',
5964
+ tone: {type: 'normal_skin_tone', codepoints: '', shortcodes: ''},
5965
+ },
5966
+ participantId: meeting.members.selfId,
5967
+ });
5968
+ });
5969
+
5970
+ it('should send a reaction with default skin tone if none provided', async () => {
5971
+ meeting.locusInfo.controls = {reactions: {reactionChannelUrl: 'Fake URL'}};
5972
+
5973
+ const reactionPromise = meeting.sendReaction('thumbs_down');
5974
+
5975
+ assert.exists(reactionPromise.then);
5976
+ await reactionPromise;
5977
+ assert.calledOnceWithExactly(meeting.meetingRequest.sendReaction, {
5978
+ reactionChannelUrl: 'Fake URL',
5979
+ reaction: {
5980
+ type: 'thumb_down',
5981
+ codepoints: '1F44E',
5982
+ shortcodes: ':thumbsdown:',
5983
+ tone: {type: 'normal_skin_tone', codepoints: '', shortcodes: ''},
5984
+ },
5985
+ participantId: meeting.members.selfId,
5986
+ });
5987
+ });
5988
+ });
5989
+ describe('#toggleReactions', () => {
5990
+ it('should have #toggleReactions', () => {
5991
+ assert.exists(meeting.toggleReactions);
5992
+ });
5993
+
5994
+ beforeEach(() => {
5995
+ meeting.meetingRequest.toggleReactions = sinon.stub().returns(Promise.resolve());
5996
+ });
5997
+
5998
+ it('should toggle the reactions with the right data and return a promise', async () => {
5999
+ meeting.locusUrl = 'locusUrl';
6000
+ meeting.locusInfo.controls = {reactions: {enabled: false}};
6001
+
6002
+ const togglePromise = meeting.toggleReactions(true);
6003
+
6004
+ assert.exists(togglePromise.then);
6005
+ await togglePromise;
6006
+ assert.calledOnceWithExactly(meeting.meetingRequest.toggleReactions, {
6007
+ locusUrl: 'locusUrl',
6008
+ enable: true,
6009
+ requestingParticipantId: meeting.members.selfId,
6010
+ });
6011
+ });
6012
+
6013
+ it('should resolve immediately if already enabled', async () => {
6014
+ meeting.locusUrl = 'locusUrl';
6015
+ meeting.locusInfo.controls = {reactions: {enabled: true}};
6016
+
6017
+ const togglePromise = meeting.toggleReactions(true);
6018
+
6019
+ const response = await togglePromise;
6020
+
6021
+ assert.equal(response, 'Reactions are already enabled.');
6022
+ assert.notCalled(meeting.meetingRequest.toggleReactions);
6023
+ });
6024
+
6025
+ it('should resolve immediately if already disabled', async () => {
6026
+ meeting.locusUrl = 'locusUrl';
6027
+ meeting.locusInfo.controls = {reactions: {enabled: false}};
6028
+
6029
+ const togglePromise = meeting.toggleReactions(false);
6030
+
6031
+ const response = await togglePromise;
6032
+
6033
+ assert.equal(response, 'Reactions are already disabled.');
6034
+ assert.notCalled(meeting.meetingRequest.toggleReactions);
6035
+ });
6036
+
6037
+ it('should toggle reactions on if controls is undefined and enable = true', async () => {
6038
+ meeting.locusUrl = 'locusUrl';
6039
+ meeting.locusInfo.controls = undefined;
6040
+
6041
+ const togglePromise = meeting.toggleReactions(true);
6042
+
6043
+ assert.exists(togglePromise.then);
6044
+ await togglePromise;
6045
+ assert.calledOnceWithExactly(meeting.meetingRequest.toggleReactions, {
6046
+ locusUrl: 'locusUrl',
6047
+ enable: true,
6048
+ requestingParticipantId: meeting.members.selfId,
6049
+ });
6050
+ });
6051
+ });
4777
6052
  });
4778
6053
  });
4779
6054
  });