@webex/plugin-meetings 3.0.0-test.1 → 3.1.0

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 (431) hide show
  1. package/.eslintrc.js +6 -0
  2. package/babel.config.js +3 -0
  3. package/dist/annotation/constants.js +12 -20
  4. package/dist/annotation/constants.js.map +1 -1
  5. package/dist/annotation/index.js +25 -10
  6. package/dist/annotation/index.js.map +1 -1
  7. package/dist/breakouts/breakout.js +2 -3
  8. package/dist/breakouts/breakout.js.map +1 -1
  9. package/dist/breakouts/collection.js +1 -2
  10. package/dist/breakouts/collection.js.map +1 -1
  11. package/dist/breakouts/edit-lock-error.js +1 -2
  12. package/dist/breakouts/edit-lock-error.js.map +1 -1
  13. package/dist/breakouts/events.js +1 -2
  14. package/dist/breakouts/events.js.map +1 -1
  15. package/dist/breakouts/index.js +13 -14
  16. package/dist/breakouts/index.js.map +1 -1
  17. package/dist/breakouts/request.js +1 -2
  18. package/dist/breakouts/request.js.map +1 -1
  19. package/dist/breakouts/utils.js +3 -6
  20. package/dist/breakouts/utils.js.map +1 -1
  21. package/dist/common/browser-detection.js +2 -3
  22. package/dist/common/browser-detection.js.map +1 -1
  23. package/dist/common/collection.js +3 -4
  24. package/dist/common/collection.js.map +1 -1
  25. package/dist/common/config.js +1 -2
  26. package/dist/common/config.js.map +1 -1
  27. package/dist/common/errors/captcha-error.js +1 -2
  28. package/dist/common/errors/captcha-error.js.map +1 -1
  29. package/dist/common/errors/intent-to-join.js +1 -2
  30. package/dist/common/errors/intent-to-join.js.map +1 -1
  31. package/dist/common/errors/join-meeting.js +1 -2
  32. package/dist/common/errors/join-meeting.js.map +1 -1
  33. package/dist/common/errors/media.js +1 -2
  34. package/dist/common/errors/media.js.map +1 -1
  35. package/dist/common/errors/no-meeting-info.js +1 -2
  36. package/dist/common/errors/no-meeting-info.js.map +1 -1
  37. package/dist/common/errors/parameter.js +3 -4
  38. package/dist/common/errors/parameter.js.map +1 -1
  39. package/dist/common/errors/password-error.js +1 -2
  40. package/dist/common/errors/password-error.js.map +1 -1
  41. package/dist/common/errors/permission.js +1 -2
  42. package/dist/common/errors/permission.js.map +1 -1
  43. package/dist/common/errors/reclaim-host-role-errors.js +6 -10
  44. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -1
  45. package/dist/common/errors/reconnection-in-progress.js +1 -2
  46. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  47. package/dist/common/errors/reconnection.js +1 -2
  48. package/dist/common/errors/reconnection.js.map +1 -1
  49. package/dist/common/errors/stats.js +1 -2
  50. package/dist/common/errors/stats.js.map +1 -1
  51. package/dist/common/errors/webex-errors.js +14 -15
  52. package/dist/common/errors/webex-errors.js.map +1 -1
  53. package/dist/common/errors/webex-meetings-error.js +1 -2
  54. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  55. package/dist/common/events/events-scope.js +1 -2
  56. package/dist/common/events/events-scope.js.map +1 -1
  57. package/dist/common/events/events.js +1 -2
  58. package/dist/common/events/events.js.map +1 -1
  59. package/dist/common/events/trigger-proxy.js +1 -2
  60. package/dist/common/events/trigger-proxy.js.map +1 -1
  61. package/dist/common/events/util.js +1 -2
  62. package/dist/common/events/util.js.map +1 -1
  63. package/dist/common/logs/logger-config.js +1 -2
  64. package/dist/common/logs/logger-config.js.map +1 -1
  65. package/dist/common/logs/logger-proxy.js +1 -2
  66. package/dist/common/logs/logger-proxy.js.map +1 -1
  67. package/dist/{types/common → common}/logs/request.d.ts +1 -1
  68. package/dist/common/logs/request.js +3 -4
  69. package/dist/common/logs/request.js.map +1 -1
  70. package/dist/common/queue.js +2 -4
  71. package/dist/common/queue.js.map +1 -1
  72. package/dist/{types/config.d.ts → config.d.ts} +1 -0
  73. package/dist/config.js +4 -3
  74. package/dist/config.js.map +1 -1
  75. package/dist/{types/constants.d.ts → constants.d.ts} +10 -5
  76. package/dist/constants.js +207 -380
  77. package/dist/constants.js.map +1 -1
  78. package/dist/controls-options-manager/constants.js +3 -6
  79. package/dist/controls-options-manager/constants.js.map +1 -1
  80. package/dist/controls-options-manager/enums.js +7 -10
  81. package/dist/controls-options-manager/enums.js.map +1 -1
  82. package/dist/controls-options-manager/index.js +27 -32
  83. package/dist/controls-options-manager/index.js.map +1 -1
  84. package/dist/controls-options-manager/util.js +1 -2
  85. package/dist/controls-options-manager/util.js.map +1 -1
  86. package/dist/index.js +3 -4
  87. package/dist/index.js.map +1 -1
  88. package/dist/interceptors/index.js.map +1 -1
  89. package/dist/interceptors/locusRetry.js +4 -3
  90. package/dist/interceptors/locusRetry.js.map +1 -1
  91. package/dist/interpretation/collection.js +1 -2
  92. package/dist/interpretation/collection.js.map +1 -1
  93. package/dist/interpretation/index.js +4 -5
  94. package/dist/interpretation/index.js.map +1 -1
  95. package/dist/interpretation/siLanguage.js +2 -3
  96. package/dist/interpretation/siLanguage.js.map +1 -1
  97. package/dist/locus-info/controlsUtils.js +12 -13
  98. package/dist/locus-info/controlsUtils.js.map +1 -1
  99. package/dist/locus-info/embeddedAppsUtils.js +3 -4
  100. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  101. package/dist/locus-info/fullState.js +1 -2
  102. package/dist/locus-info/fullState.js.map +1 -1
  103. package/dist/locus-info/hostUtils.js +1 -2
  104. package/dist/locus-info/hostUtils.js.map +1 -1
  105. package/dist/locus-info/index.js +23 -27
  106. package/dist/locus-info/index.js.map +1 -1
  107. package/dist/locus-info/infoUtils.js +3 -4
  108. package/dist/locus-info/infoUtils.js.map +1 -1
  109. package/dist/locus-info/mediaSharesUtils.js +16 -3
  110. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  111. package/dist/locus-info/parser.js +6 -10
  112. package/dist/locus-info/parser.js.map +1 -1
  113. package/dist/locus-info/selfUtils.js +12 -6
  114. package/dist/locus-info/selfUtils.js.map +1 -1
  115. package/dist/media/MediaConnectionAwaiter.d.ts +61 -0
  116. package/dist/media/MediaConnectionAwaiter.js +163 -0
  117. package/dist/media/MediaConnectionAwaiter.js.map +1 -0
  118. package/dist/media/index.js +9 -5
  119. package/dist/media/index.js.map +1 -1
  120. package/dist/media/properties.js +7 -28
  121. package/dist/media/properties.js.map +1 -1
  122. package/dist/media/util.js +1 -2
  123. package/dist/media/util.js.map +1 -1
  124. package/dist/mediaQualityMetrics/config.js +9 -18
  125. package/dist/mediaQualityMetrics/config.js.map +1 -1
  126. package/dist/meeting/in-meeting-actions.js +14 -2
  127. package/dist/meeting/in-meeting-actions.js.map +1 -1
  128. package/dist/{types/meeting → meeting}/index.d.ts +71 -16
  129. package/dist/meeting/index.js +1411 -1036
  130. package/dist/meeting/index.js.map +1 -1
  131. package/dist/meeting/locusMediaRequest.js +4 -5
  132. package/dist/meeting/locusMediaRequest.js.map +1 -1
  133. package/dist/meeting/muteState.js +2 -4
  134. package/dist/meeting/muteState.js.map +1 -1
  135. package/dist/{types/meeting → meeting}/request.d.ts +3 -0
  136. package/dist/meeting/request.js +45 -36
  137. package/dist/meeting/request.js.map +1 -1
  138. package/dist/meeting/state.js +1 -2
  139. package/dist/meeting/state.js.map +1 -1
  140. package/dist/{types/meeting → meeting}/util.d.ts +1 -0
  141. package/dist/meeting/util.js +13 -10
  142. package/dist/meeting/util.js.map +1 -1
  143. package/dist/meeting/voicea-meeting.d.ts +16 -0
  144. package/dist/meeting/voicea-meeting.js +169 -0
  145. package/dist/meeting/voicea-meeting.js.map +1 -0
  146. package/dist/meeting-info/collection.js +3 -4
  147. package/dist/meeting-info/collection.js.map +1 -1
  148. package/dist/meeting-info/index.js +5 -4
  149. package/dist/meeting-info/index.js.map +1 -1
  150. package/dist/meeting-info/meeting-info-v2.js +27 -29
  151. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  152. package/dist/meeting-info/request.js +1 -2
  153. package/dist/meeting-info/request.js.map +1 -1
  154. package/dist/meeting-info/util.js +8 -8
  155. package/dist/meeting-info/util.js.map +1 -1
  156. package/dist/meeting-info/utilv2.js +15 -9
  157. package/dist/meeting-info/utilv2.js.map +1 -1
  158. package/dist/meetings/collection.js +5 -6
  159. package/dist/meetings/collection.js.map +1 -1
  160. package/dist/{types/meetings → meetings}/index.d.ts +11 -2
  161. package/dist/meetings/index.js +44 -17
  162. package/dist/meetings/index.js.map +1 -1
  163. package/dist/meetings/request.js +2 -3
  164. package/dist/meetings/request.js.map +1 -1
  165. package/dist/meetings/util.js +1 -4
  166. package/dist/meetings/util.js.map +1 -1
  167. package/dist/member/index.js +1 -3
  168. package/dist/member/index.js.map +1 -1
  169. package/dist/member/types.js +6 -8
  170. package/dist/member/types.js.map +1 -1
  171. package/dist/member/util.js +1 -2
  172. package/dist/member/util.js.map +1 -1
  173. package/dist/members/collection.js +1 -2
  174. package/dist/members/collection.js.map +1 -1
  175. package/dist/members/index.js +8 -7
  176. package/dist/members/index.js.map +1 -1
  177. package/dist/members/request.js +2 -3
  178. package/dist/members/request.js.map +1 -1
  179. package/dist/members/types.js +3 -4
  180. package/dist/members/types.js.map +1 -1
  181. package/dist/{types/members → members}/util.d.ts +1 -1
  182. package/dist/members/util.js +3 -4
  183. package/dist/members/util.js.map +1 -1
  184. package/dist/metrics/constants.js +1 -2
  185. package/dist/metrics/constants.js.map +1 -1
  186. package/dist/metrics/index.js +3 -2
  187. package/dist/metrics/index.js.map +1 -1
  188. package/dist/{types/multistream → multistream}/mediaRequestManager.d.ts +1 -2
  189. package/dist/multistream/mediaRequestManager.js +9 -11
  190. package/dist/multistream/mediaRequestManager.js.map +1 -1
  191. package/dist/multistream/receiveSlot.js +3 -5
  192. package/dist/multistream/receiveSlot.js.map +1 -1
  193. package/dist/multistream/receiveSlotManager.js +7 -9
  194. package/dist/multistream/receiveSlotManager.js.map +1 -1
  195. package/dist/multistream/remoteMedia.js +3 -5
  196. package/dist/multistream/remoteMedia.js.map +1 -1
  197. package/dist/{types/multistream → multistream}/remoteMediaGroup.d.ts +1 -1
  198. package/dist/multistream/remoteMediaGroup.js +7 -6
  199. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  200. package/dist/{types/multistream → multistream}/remoteMediaManager.d.ts +1 -2
  201. package/dist/multistream/remoteMediaManager.js +32 -29
  202. package/dist/multistream/remoteMediaManager.js.map +1 -1
  203. package/dist/{types/multistream → multistream}/sendSlotManager.d.ts +1 -2
  204. package/dist/multistream/sendSlotManager.js +9 -6
  205. package/dist/multistream/sendSlotManager.js.map +1 -1
  206. package/dist/networkQualityMonitor/index.js +1 -2
  207. package/dist/networkQualityMonitor/index.js.map +1 -1
  208. package/dist/personal-meeting-room/index.js +2 -3
  209. package/dist/personal-meeting-room/index.js.map +1 -1
  210. package/dist/personal-meeting-room/request.js +2 -3
  211. package/dist/personal-meeting-room/request.js.map +1 -1
  212. package/dist/personal-meeting-room/util.js +1 -2
  213. package/dist/personal-meeting-room/util.js.map +1 -1
  214. package/dist/{types/reachability → reachability}/clusterReachability.d.ts +1 -0
  215. package/dist/reachability/clusterReachability.js +35 -20
  216. package/dist/reachability/clusterReachability.js.map +1 -1
  217. package/dist/{types/reachability → reachability}/index.d.ts +4 -0
  218. package/dist/reachability/index.js +41 -12
  219. package/dist/reachability/index.js.map +1 -1
  220. package/dist/reachability/request.js +25 -20
  221. package/dist/reachability/request.js.map +1 -1
  222. package/dist/{types/reachability → reachability}/util.d.ts +7 -0
  223. package/dist/reachability/util.js +19 -0
  224. package/dist/reachability/util.js.map +1 -1
  225. package/dist/reactions/constants.js +1 -2
  226. package/dist/reactions/constants.js.map +1 -1
  227. package/dist/reactions/reactions.js +2 -4
  228. package/dist/reactions/reactions.js.map +1 -1
  229. package/dist/reactions/reactions.type.js +6 -8
  230. package/dist/reactions/reactions.type.js.map +1 -1
  231. package/dist/reconnection-manager/index.js +18 -11
  232. package/dist/reconnection-manager/index.js.map +1 -1
  233. package/dist/recording-controller/enums.js +4 -5
  234. package/dist/recording-controller/enums.js.map +1 -1
  235. package/dist/recording-controller/index.js +43 -51
  236. package/dist/recording-controller/index.js.map +1 -1
  237. package/dist/recording-controller/util.js +1 -2
  238. package/dist/recording-controller/util.js.map +1 -1
  239. package/dist/{types/roap → roap}/index.d.ts +10 -2
  240. package/dist/roap/index.js +17 -3
  241. package/dist/roap/index.js.map +1 -1
  242. package/dist/roap/request.js +10 -10
  243. package/dist/roap/request.js.map +1 -1
  244. package/dist/{types/roap → roap}/turnDiscovery.d.ts +64 -17
  245. package/dist/roap/turnDiscovery.js +316 -134
  246. package/dist/roap/turnDiscovery.js.map +1 -1
  247. package/dist/rtcMetrics/constants.js +1 -2
  248. package/dist/rtcMetrics/constants.js.map +1 -1
  249. package/dist/rtcMetrics/index.js +4 -6
  250. package/dist/rtcMetrics/index.js.map +1 -1
  251. package/dist/statsAnalyzer/global.js +1 -2
  252. package/dist/statsAnalyzer/global.js.map +1 -1
  253. package/dist/statsAnalyzer/index.js +123 -96
  254. package/dist/statsAnalyzer/index.js.map +1 -1
  255. package/dist/statsAnalyzer/mqaUtil.js +24 -31
  256. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  257. package/dist/transcription/index.js +1 -2
  258. package/dist/transcription/index.js.map +1 -1
  259. package/dist/webinar/collection.js +1 -2
  260. package/dist/webinar/collection.js.map +1 -1
  261. package/dist/webinar/index.js +2 -3
  262. package/dist/webinar/index.js.map +1 -1
  263. package/jest.config.js +3 -0
  264. package/package.json +44 -25
  265. package/process +1 -0
  266. package/src/config.ts +3 -4
  267. package/src/constants.ts +13 -4
  268. package/src/interpretation/index.ts +2 -2
  269. package/src/locus-info/mediaSharesUtils.ts +16 -0
  270. package/src/locus-info/selfUtils.ts +5 -0
  271. package/src/media/MediaConnectionAwaiter.ts +174 -0
  272. package/src/media/index.ts +3 -1
  273. package/src/media/properties.ts +6 -31
  274. package/src/meeting/index.ts +526 -227
  275. package/src/meeting/request.ts +18 -2
  276. package/src/meeting/util.ts +6 -1
  277. package/src/meeting/voicea-meeting.ts +122 -0
  278. package/src/meeting-info/meeting-info-v2.ts +5 -11
  279. package/src/meeting-info/util.ts +12 -9
  280. package/src/meeting-info/utilv2.ts +26 -15
  281. package/src/meetings/index.ts +18 -0
  282. package/src/member/index.ts +0 -1
  283. package/src/multistream/mediaRequestManager.ts +1 -1
  284. package/src/multistream/remoteMediaGroup.ts +1 -1
  285. package/src/multistream/remoteMediaManager.ts +1 -2
  286. package/src/multistream/sendSlotManager.ts +1 -2
  287. package/src/reachability/clusterReachability.ts +20 -5
  288. package/src/reachability/index.ts +24 -1
  289. package/src/reachability/request.ts +15 -11
  290. package/src/reachability/util.ts +21 -0
  291. package/src/reconnection-manager/index.ts +1 -1
  292. package/src/roap/index.ts +25 -3
  293. package/src/roap/request.ts +2 -2
  294. package/src/roap/turnDiscovery.ts +244 -78
  295. package/src/statsAnalyzer/index.ts +63 -27
  296. package/test/integration/spec/journey.js +2 -2
  297. package/test/unit/spec/breakouts/breakout.ts +2 -1
  298. package/test/unit/spec/breakouts/index.ts +7 -4
  299. package/test/unit/spec/interpretation/index.ts +4 -1
  300. package/test/unit/spec/locus-info/index.js +27 -18
  301. package/test/unit/spec/locus-info/mediaSharesUtils.ts +9 -0
  302. package/test/unit/spec/locus-info/selfUtils.js +41 -11
  303. package/test/unit/spec/media/MediaConnectionAwaiter.ts +344 -0
  304. package/test/unit/spec/media/index.ts +94 -78
  305. package/test/unit/spec/media/properties.ts +16 -70
  306. package/test/unit/spec/meeting/index.js +757 -141
  307. package/test/unit/spec/meeting/request.js +21 -0
  308. package/test/unit/spec/meeting/utils.js +58 -11
  309. package/test/unit/spec/meeting/voicea-meeting.ts +266 -0
  310. package/test/unit/spec/meeting-info/meetinginfov2.js +20 -15
  311. package/test/unit/spec/meeting-info/utilv2.js +6 -0
  312. package/test/unit/spec/meetings/index.js +101 -13
  313. package/test/unit/spec/metrics/index.js +1 -2
  314. package/test/unit/spec/multistream/mediaRequestManager.ts +1 -0
  315. package/test/unit/spec/multistream/remoteMediaGroup.ts +0 -1
  316. package/test/unit/spec/multistream/remoteMediaManager.ts +0 -1
  317. package/test/unit/spec/reachability/clusterReachability.ts +86 -22
  318. package/test/unit/spec/reachability/index.ts +197 -60
  319. package/test/unit/spec/reachability/request.js +15 -7
  320. package/test/unit/spec/reachability/util.ts +32 -2
  321. package/test/unit/spec/reconnection-manager/index.js +28 -0
  322. package/test/unit/spec/recording-controller/index.js +0 -1
  323. package/test/unit/spec/roap/index.ts +61 -6
  324. package/test/unit/spec/roap/turnDiscovery.ts +299 -17
  325. package/test/unit/spec/stats-analyzer/index.js +179 -0
  326. /package/dist/{types/annotation → annotation}/annotation.types.d.ts +0 -0
  327. /package/dist/{types/annotation → annotation}/constants.d.ts +0 -0
  328. /package/dist/{types/annotation → annotation}/index.d.ts +0 -0
  329. /package/dist/{types/breakouts → breakouts}/breakout.d.ts +0 -0
  330. /package/dist/{types/breakouts → breakouts}/collection.d.ts +0 -0
  331. /package/dist/{types/breakouts → breakouts}/edit-lock-error.d.ts +0 -0
  332. /package/dist/{types/breakouts → breakouts}/events.d.ts +0 -0
  333. /package/dist/{types/breakouts → breakouts}/index.d.ts +0 -0
  334. /package/dist/{types/breakouts → breakouts}/request.d.ts +0 -0
  335. /package/dist/{types/breakouts → breakouts}/utils.d.ts +0 -0
  336. /package/dist/{types/common → common}/browser-detection.d.ts +0 -0
  337. /package/dist/{types/common → common}/collection.d.ts +0 -0
  338. /package/dist/{types/common → common}/config.d.ts +0 -0
  339. /package/dist/{types/common → common}/errors/captcha-error.d.ts +0 -0
  340. /package/dist/{types/common → common}/errors/intent-to-join.d.ts +0 -0
  341. /package/dist/{types/common → common}/errors/join-meeting.d.ts +0 -0
  342. /package/dist/{types/common → common}/errors/media.d.ts +0 -0
  343. /package/dist/{types/common → common}/errors/no-meeting-info.d.ts +0 -0
  344. /package/dist/{types/common → common}/errors/parameter.d.ts +0 -0
  345. /package/dist/{types/common → common}/errors/password-error.d.ts +0 -0
  346. /package/dist/{types/common → common}/errors/permission.d.ts +0 -0
  347. /package/dist/{types/common → common}/errors/reclaim-host-role-errors.d.ts +0 -0
  348. /package/dist/{types/common → common}/errors/reconnection-in-progress.d.ts +0 -0
  349. /package/dist/{types/common → common}/errors/reconnection.d.ts +0 -0
  350. /package/dist/{types/common → common}/errors/stats.d.ts +0 -0
  351. /package/dist/{types/common → common}/errors/webex-errors.d.ts +0 -0
  352. /package/dist/{types/common → common}/errors/webex-meetings-error.d.ts +0 -0
  353. /package/dist/{types/common → common}/events/events-scope.d.ts +0 -0
  354. /package/dist/{types/common → common}/events/events.d.ts +0 -0
  355. /package/dist/{types/common → common}/events/trigger-proxy.d.ts +0 -0
  356. /package/dist/{types/common → common}/events/util.d.ts +0 -0
  357. /package/dist/{types/common → common}/logs/logger-config.d.ts +0 -0
  358. /package/dist/{types/common → common}/logs/logger-proxy.d.ts +0 -0
  359. /package/dist/{types/common → common}/queue.d.ts +0 -0
  360. /package/dist/{types/controls-options-manager → controls-options-manager}/constants.d.ts +0 -0
  361. /package/dist/{types/controls-options-manager → controls-options-manager}/enums.d.ts +0 -0
  362. /package/dist/{types/controls-options-manager → controls-options-manager}/index.d.ts +0 -0
  363. /package/dist/{types/controls-options-manager → controls-options-manager}/types.d.ts +0 -0
  364. /package/dist/{types/controls-options-manager → controls-options-manager}/util.d.ts +0 -0
  365. /package/dist/{types/index.d.ts → index.d.ts} +0 -0
  366. /package/dist/{types/interceptors → interceptors}/index.d.ts +0 -0
  367. /package/dist/{types/interceptors → interceptors}/locusRetry.d.ts +0 -0
  368. /package/dist/{types/interpretation → interpretation}/collection.d.ts +0 -0
  369. /package/dist/{types/interpretation → interpretation}/index.d.ts +0 -0
  370. /package/dist/{types/interpretation → interpretation}/siLanguage.d.ts +0 -0
  371. /package/dist/{types/locus-info → locus-info}/controlsUtils.d.ts +0 -0
  372. /package/dist/{types/locus-info → locus-info}/embeddedAppsUtils.d.ts +0 -0
  373. /package/dist/{types/locus-info → locus-info}/fullState.d.ts +0 -0
  374. /package/dist/{types/locus-info → locus-info}/hostUtils.d.ts +0 -0
  375. /package/dist/{types/locus-info → locus-info}/index.d.ts +0 -0
  376. /package/dist/{types/locus-info → locus-info}/infoUtils.d.ts +0 -0
  377. /package/dist/{types/locus-info → locus-info}/mediaSharesUtils.d.ts +0 -0
  378. /package/dist/{types/locus-info → locus-info}/parser.d.ts +0 -0
  379. /package/dist/{types/locus-info → locus-info}/selfUtils.d.ts +0 -0
  380. /package/dist/{types/media → media}/index.d.ts +0 -0
  381. /package/dist/{types/media → media}/properties.d.ts +0 -0
  382. /package/dist/{types/media → media}/util.d.ts +0 -0
  383. /package/dist/{types/mediaQualityMetrics → mediaQualityMetrics}/config.d.ts +0 -0
  384. /package/dist/{types/meeting → meeting}/in-meeting-actions.d.ts +0 -0
  385. /package/dist/{types/meeting → meeting}/locusMediaRequest.d.ts +0 -0
  386. /package/dist/{types/meeting → meeting}/muteState.d.ts +0 -0
  387. /package/dist/{types/meeting → meeting}/request.type.d.ts +0 -0
  388. /package/dist/{types/meeting → meeting}/state.d.ts +0 -0
  389. /package/dist/{types/meeting-info → meeting-info}/collection.d.ts +0 -0
  390. /package/dist/{types/meeting-info → meeting-info}/index.d.ts +0 -0
  391. /package/dist/{types/meeting-info → meeting-info}/meeting-info-v2.d.ts +0 -0
  392. /package/dist/{types/meeting-info → meeting-info}/request.d.ts +0 -0
  393. /package/dist/{types/meeting-info → meeting-info}/util.d.ts +0 -0
  394. /package/dist/{types/meeting-info → meeting-info}/utilv2.d.ts +0 -0
  395. /package/dist/{types/meetings → meetings}/collection.d.ts +0 -0
  396. /package/dist/{types/meetings → meetings}/meetings.types.d.ts +0 -0
  397. /package/dist/{types/meetings → meetings}/request.d.ts +0 -0
  398. /package/dist/{types/meetings → meetings}/util.d.ts +0 -0
  399. /package/dist/{types/member → member}/index.d.ts +0 -0
  400. /package/dist/{types/member → member}/types.d.ts +0 -0
  401. /package/dist/{types/member → member}/util.d.ts +0 -0
  402. /package/dist/{types/members → members}/collection.d.ts +0 -0
  403. /package/dist/{types/members → members}/index.d.ts +0 -0
  404. /package/dist/{types/members → members}/request.d.ts +0 -0
  405. /package/dist/{types/members → members}/types.d.ts +0 -0
  406. /package/dist/{types/metrics → metrics}/constants.d.ts +0 -0
  407. /package/dist/{types/metrics → metrics}/index.d.ts +0 -0
  408. /package/dist/{types/multistream → multistream}/receiveSlot.d.ts +0 -0
  409. /package/dist/{types/multistream → multistream}/receiveSlotManager.d.ts +0 -0
  410. /package/dist/{types/multistream → multistream}/remoteMedia.d.ts +0 -0
  411. /package/dist/{types/networkQualityMonitor → networkQualityMonitor}/index.d.ts +0 -0
  412. /package/dist/{types/personal-meeting-room → personal-meeting-room}/index.d.ts +0 -0
  413. /package/dist/{types/personal-meeting-room → personal-meeting-room}/request.d.ts +0 -0
  414. /package/dist/{types/personal-meeting-room → personal-meeting-room}/util.d.ts +0 -0
  415. /package/dist/{types/reachability → reachability}/request.d.ts +0 -0
  416. /package/dist/{types/reactions → reactions}/constants.d.ts +0 -0
  417. /package/dist/{types/reactions → reactions}/reactions.d.ts +0 -0
  418. /package/dist/{types/reactions → reactions}/reactions.type.d.ts +0 -0
  419. /package/dist/{types/reconnection-manager → reconnection-manager}/index.d.ts +0 -0
  420. /package/dist/{types/recording-controller → recording-controller}/enums.d.ts +0 -0
  421. /package/dist/{types/recording-controller → recording-controller}/index.d.ts +0 -0
  422. /package/dist/{types/recording-controller → recording-controller}/util.d.ts +0 -0
  423. /package/dist/{types/roap → roap}/request.d.ts +0 -0
  424. /package/dist/{types/rtcMetrics → rtcMetrics}/constants.d.ts +0 -0
  425. /package/dist/{types/rtcMetrics → rtcMetrics}/index.d.ts +0 -0
  426. /package/dist/{types/statsAnalyzer → statsAnalyzer}/global.d.ts +0 -0
  427. /package/dist/{types/statsAnalyzer → statsAnalyzer}/index.d.ts +0 -0
  428. /package/dist/{types/statsAnalyzer → statsAnalyzer}/mqaUtil.d.ts +0 -0
  429. /package/dist/{types/transcription → transcription}/index.d.ts +0 -0
  430. /package/dist/{types/webinar → webinar}/collection.d.ts +0 -0
  431. /package/dist/{types/webinar → webinar}/index.d.ts +0 -0
@@ -2,7 +2,6 @@
2
2
  * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
3
  */
4
4
  import 'jsdom-global/register';
5
- import jwt from 'jsonwebtoken';
6
5
  import {cloneDeep, forEach, isEqual, isUndefined} from 'lodash';
7
6
  import sinon from 'sinon';
8
7
  import * as internalMediaModule from '@webex/internal-media-core';
@@ -100,7 +99,6 @@ import {
100
99
  MeetingInfoV2PolicyError,
101
100
  } from '../../../../src/meeting-info/meeting-info-v2';
102
101
  import {
103
- CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD,
104
102
  DTLS_HANDSHAKE_FAILED_CLIENT_CODE,
105
103
  ICE_FAILED_WITHOUT_TURN_TLS_CLIENT_CODE,
106
104
  ICE_FAILED_WITH_TURN_TLS_CLIENT_CODE,
@@ -111,6 +109,8 @@ import CallDiagnosticMetrics from '@webex/internal-plugin-metrics/src/call-diagn
111
109
  import {ERROR_DESCRIPTIONS} from '@webex/internal-plugin-metrics/src/call-diagnostic/config';
112
110
  import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
113
111
 
112
+ import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
113
+
114
114
  describe('plugin-meetings', () => {
115
115
  const logger = {
116
116
  info: () => {},
@@ -610,36 +610,177 @@ describe('plugin-meetings', () => {
610
610
  assert.exists(meeting.joinWithMedia);
611
611
  });
612
612
 
613
- describe('resolution', () => {
614
- it('should success and return a promise', async () => {
615
- meeting.join = sinon.stub().returns(Promise.resolve(test1));
616
- meeting.addMedia = sinon.stub().returns(Promise.resolve(test4));
613
+ const fakeRoapMessage = {id: 'fake TURN discovery message'};
614
+ const fakeReachabilityResults = {id: 'fake reachability'};
615
+ const fakeTurnServerInfo = {id: 'fake turn info'};
616
+ const fakeJoinResult = {id: 'join result'};
617
617
 
618
- const joinOptions = {correlationId: '12345'};
619
- const mediaOptions = {audioEnabled: test1, allowMediaInLobby: true};
618
+ const joinOptions = {correlationId: '12345'};
619
+ const mediaOptions = {audioEnabled: true, allowMediaInLobby: true};
620
620
 
621
- const result = await meeting.joinWithMedia({
622
- joinOptions,
623
- mediaOptions,
624
- });
625
- assert.calledOnceWithExactly(meeting.join, joinOptions);
626
- assert.calledOnceWithExactly(meeting.addMedia, mediaOptions);
627
- assert.deepEqual(result, {join: test1, media: test4});
621
+ let generateTurnDiscoveryRequestMessageStub;
622
+ let handleTurnDiscoveryHttpResponseStub;
623
+ let abortTurnDiscoveryStub;
624
+
625
+ beforeEach(() => {
626
+ meeting.join = sinon.stub().returns(Promise.resolve(fakeJoinResult));
627
+ meeting.addMedia = sinon.stub().returns(Promise.resolve(test4));
628
+
629
+ webex.meetings.reachability.getReachabilityResults.resolves(fakeReachabilityResults);
630
+
631
+ generateTurnDiscoveryRequestMessageStub = sinon
632
+ .stub(meeting.roap, 'generateTurnDiscoveryRequestMessage')
633
+ .resolves({roapMessage: fakeRoapMessage});
634
+ handleTurnDiscoveryHttpResponseStub = sinon
635
+ .stub(meeting.roap, 'handleTurnDiscoveryHttpResponse')
636
+ .resolves({turnServerInfo: fakeTurnServerInfo, turnDiscoverySkippedReason: undefined});
637
+ abortTurnDiscoveryStub = sinon.stub(meeting.roap, 'abortTurnDiscovery');
638
+ });
639
+
640
+ it('should work as expected', async () => {
641
+ const result = await meeting.joinWithMedia({
642
+ joinOptions,
643
+ mediaOptions,
644
+ });
645
+
646
+ // check that TURN discovery is done with join and addMedia called
647
+ assert.calledOnceWithExactly(meeting.join, {
648
+ ...joinOptions,
649
+ roapMessage: fakeRoapMessage,
650
+ reachability: fakeReachabilityResults,
651
+ });
652
+ assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, true);
653
+ assert.calledOnceWithExactly(
654
+ handleTurnDiscoveryHttpResponseStub,
655
+ meeting,
656
+ fakeJoinResult
657
+ );
658
+ assert.calledOnceWithExactly(meeting.addMedia, mediaOptions, fakeTurnServerInfo);
659
+
660
+ assert.deepEqual(result, {join: fakeJoinResult, media: test4});
661
+ });
662
+
663
+ it("should not call handleTurnDiscoveryHttpResponse if we don't send a TURN discovery request with join", async () => {
664
+ generateTurnDiscoveryRequestMessageStub.resolves({roapMessage: undefined});
665
+
666
+ const result = await meeting.joinWithMedia({
667
+ joinOptions,
668
+ mediaOptions,
669
+ });
670
+
671
+ // check that TURN discovery is done with join and addMedia called
672
+ assert.calledOnceWithExactly(meeting.join, {
673
+ ...joinOptions,
674
+ roapMessage: undefined,
675
+ reachability: fakeReachabilityResults,
628
676
  });
677
+ assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, true);
678
+ assert.notCalled(handleTurnDiscoveryHttpResponseStub);
679
+ assert.notCalled(abortTurnDiscoveryStub);
680
+ assert.calledOnceWithExactly(meeting.addMedia, mediaOptions, undefined);
681
+
682
+ assert.deepEqual(result, {join: fakeJoinResult, media: test4});
683
+ assert.equal(meeting.turnServerUsed, false);
629
684
  });
630
685
 
631
- describe('rejection', () => {
632
- it('should error out and return a promise', async () => {
633
- meeting.join = sinon.stub().returns(Promise.reject());
634
- assert.isRejected(meeting.joinWithMedia({mediaOptions: {allowMediaInLobby: true}}));
686
+ it('should call abortTurnDiscovery() if we do not get a TURN server info', async () => {
687
+ handleTurnDiscoveryHttpResponseStub.resolves({
688
+ turnServerInfo: undefined,
689
+ turnDiscoverySkippedReason: 'missing http response',
635
690
  });
636
691
 
637
- it('should fail if called with allowMediaInLobby:false', async () => {
638
- meeting.join = sinon.stub().returns(Promise.resolve(test1));
639
- meeting.addMedia = sinon.stub().returns(Promise.resolve(test4));
692
+ const result = await meeting.joinWithMedia({
693
+ joinOptions,
694
+ mediaOptions,
695
+ });
640
696
 
641
- assert.isRejected(meeting.joinWithMedia({mediaOptions: {allowMediaInLobby: false}}));
697
+ // check that TURN discovery is done with join and addMedia called
698
+ assert.calledOnceWithExactly(meeting.join, {
699
+ ...joinOptions,
700
+ roapMessage: fakeRoapMessage,
701
+ reachability: fakeReachabilityResults,
642
702
  });
703
+ assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, true);
704
+ assert.calledOnceWithExactly(
705
+ handleTurnDiscoveryHttpResponseStub,
706
+ meeting,
707
+ fakeJoinResult
708
+ );
709
+ assert.calledOnceWithExactly(abortTurnDiscoveryStub);
710
+ assert.calledOnceWithExactly(meeting.addMedia, mediaOptions, undefined);
711
+
712
+ assert.deepEqual(result, {join: fakeJoinResult, media: test4});
713
+ });
714
+
715
+ it('should reject if join() fails', async () => {
716
+ const error = new Error('fake');
717
+ meeting.join = sinon.stub().returns(Promise.reject(error));
718
+ meeting.locusUrl = null; // when join fails, we end up with null locusUrl
719
+
720
+ await assert.isRejected(meeting.joinWithMedia({mediaOptions: {allowMediaInLobby: true}}));
721
+
722
+ assert.calledOnceWithExactly(abortTurnDiscoveryStub);
723
+
724
+ assert.calledWith(
725
+ Metrics.sendBehavioralMetric,
726
+ BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
727
+ {
728
+ correlation_id: meeting.correlationId,
729
+ locus_id: undefined,
730
+ reason: error.message,
731
+ stack: error.stack,
732
+ leaveErrorReason: undefined,
733
+ },
734
+ {
735
+ type: error.name,
736
+ }
737
+ );
738
+ });
739
+
740
+ it('should fail if called with allowMediaInLobby:false', async () => {
741
+ meeting.join = sinon.stub().returns(Promise.resolve(test1));
742
+ meeting.addMedia = sinon.stub().returns(Promise.resolve(test4));
743
+
744
+ await assert.isRejected(
745
+ meeting.joinWithMedia({mediaOptions: {allowMediaInLobby: false}})
746
+ );
747
+ });
748
+
749
+ it('should call leave() if addMedia fails and ignore leave() failure', async () => {
750
+ const leaveError = new Error('leave error');
751
+ const addMediaError = new Error('fake addMedia error');
752
+
753
+ const leaveStub = sinon.stub(meeting, 'leave').rejects(leaveError);
754
+ meeting.addMedia = sinon.stub().rejects(addMediaError);
755
+
756
+ await assert.isRejected(
757
+ meeting.joinWithMedia({
758
+ joinOptions: {resourceId: 'some resource'},
759
+ mediaOptions: {allowMediaInLobby: true},
760
+ }),
761
+ addMediaError
762
+ );
763
+
764
+ assert.calledOnce(leaveStub);
765
+ assert.calledOnceWithExactly(leaveStub, {
766
+ resourceId: 'some resource',
767
+ reason: 'joinWithMedia failure',
768
+ });
769
+
770
+ assert.calledWith(
771
+ Metrics.sendBehavioralMetric,
772
+ BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
773
+ {
774
+ correlation_id: meeting.correlationId,
775
+ locus_id: meeting.locusUrl.split('/').pop(),
776
+ reason: addMediaError.message,
777
+ stack: addMediaError.stack,
778
+ leaveErrorReason: leaveError.message,
779
+ },
780
+ {
781
+ type: addMediaError.name,
782
+ }
783
+ );
643
784
  });
644
785
  });
645
786
 
@@ -655,38 +796,250 @@ describe('plugin-meetings', () => {
655
796
  assert.equal(meeting.isTranscriptionSupported(), true);
656
797
  });
657
798
  });
658
- describe('#receiveTranscription', () => {
659
- it('should invoke subscribe method to invoke the callback', () => {
660
- meeting.monitorTranscriptionSocketConnection = sinon.stub();
661
- meeting.initializeTranscription = sinon.stub();
662
799
 
663
- meeting.receiveTranscription().then(() => {
664
- assert.equal(true, false);
665
- assert.calledOnce(meeting.initializeTranscription);
666
- assert.calledOnce(meeting.monitorTranscriptionSocketConnection);
667
- });
800
+ describe('#startTranscription', () => {
801
+ beforeEach(() => {
802
+ webex.internal.voicea.on = sinon.stub();
803
+ webex.internal.voicea.off = sinon.stub();
804
+ webex.internal.voicea.listenToEvents = sinon.stub();
805
+ webex.internal.voicea.toggleTranscribing = sinon.stub();
806
+ });
807
+
808
+ it('should subscribe to events for the first time and avoid subscribing for future transcription starts', async () => {
809
+ meeting.joinedWith = {
810
+ state: 'JOINED',
811
+ };
812
+ meeting.areVoiceaEventsSetup = false;
813
+ meeting.roles = ['MODERATOR'];
814
+
815
+ await meeting.startTranscription();
816
+
817
+ assert.equal(webex.internal.voicea.on.callCount, 4);
818
+ assert.equal(meeting.areVoiceaEventsSetup, true);
819
+ assert.equal(webex.internal.voicea.listenToEvents.callCount, 1);
820
+ assert.calledWith(webex.internal.voicea.toggleTranscribing, true);
821
+
822
+ await meeting.startTranscription();
823
+ assert.equal(webex.internal.voicea.on.callCount, 4);
824
+ assert.equal(meeting.areVoiceaEventsSetup, true);
825
+ assert.equal(webex.internal.voicea.listenToEvents.callCount, 1);
826
+ assert.calledTwice(webex.internal.voicea.toggleTranscribing);
827
+ assert.calledWith(webex.internal.voicea.toggleTranscribing, true);
828
+ });
829
+
830
+ it('should listen to events and not toggleTranscribing if the user is not a host', async () => {
831
+ meeting.joinedWith = {
832
+ state: 'JOINED',
833
+ };
834
+ meeting.areVoiceaEventsSetup = false;
835
+ meeting.roles = ['COHOST'];
836
+
837
+ await meeting.startTranscription();
838
+
839
+ assert.equal(webex.internal.voicea.on.callCount, 4);
840
+ assert.equal(meeting.areVoiceaEventsSetup, true);
841
+ assert.equal(webex.internal.voicea.listenToEvents.callCount, 1);
842
+ assert.notCalled(webex.internal.voicea.toggleTranscribing);
668
843
  });
669
844
 
670
845
  it("should throw error if request doesn't work", async () => {
671
846
  meeting.request = sinon.stub().returns(Promise.reject());
672
847
 
673
848
  try {
674
- await meeting.receiveTranscription();
849
+ await meeting.startTranscription();
675
850
  } catch (err) {
676
851
  assert(err, {});
677
852
  }
678
853
  });
679
854
  });
680
- describe('#stopReceivingTranscription', () => {
681
- it('should get invoked', () => {
682
- meeting.transcription = {
683
- closeSocket: sinon.stub(),
855
+
856
+ describe('#stopTranscription', () => {
857
+ beforeEach(() => {
858
+ webex.internal.voicea.on = sinon.stub();
859
+ webex.internal.voicea.off = sinon.stub();
860
+ webex.internal.voicea.listenToEvents = sinon.stub();
861
+ webex.internal.voicea.toggleTranscribing = sinon.stub();
862
+ });
863
+
864
+ it('should stop listening to voicea events and also trigger a stop event', () => {
865
+ meeting.stopTranscription();
866
+ assert.equal(webex.internal.voicea.off.callCount, 4);
867
+ assert.equal(meeting.areVoiceaEventsSetup, false);
868
+ assert.calledWith(
869
+ TriggerProxy.trigger,
870
+ sinon.match.instanceOf(Meeting),
871
+ {
872
+ file: 'meeting/index',
873
+ function: 'triggerStopReceivingTranscriptionEvent',
874
+ },
875
+ EVENT_TRIGGERS.MEETING_STOPPED_RECEIVING_TRANSCRIPTION
876
+ );
877
+ });
878
+ });
879
+
880
+ describe('#setCaptionLanguage', () => {
881
+ beforeEach(() => {
882
+ meeting.isTranscriptionSupported = sinon.stub();
883
+ meeting.transcription = {languageOptions: {}};
884
+ webex.internal.voicea.on = sinon.stub();
885
+ webex.internal.voicea.off = sinon.stub();
886
+ webex.internal.voicea.setCaptionLanguage = sinon.stub();
887
+ webex.internal.voicea.requestLanguage = sinon.stub();
888
+ });
889
+
890
+ afterEach(() => {
891
+ // Restore the original methods after each test
892
+ sinon.restore();
893
+ });
894
+
895
+ it('should reject if transcription is not supported', (done) => {
896
+ meeting.isTranscriptionSupported.returns(false);
897
+
898
+ meeting.setCaptionLanguage('fr').catch((error) => {
899
+ assert.equal(error.message, 'Webex Assistant is not enabled/supported');
900
+ done();
901
+ });
902
+ });
903
+
904
+ it('should resolve with the language code on successful language update', (done) => {
905
+ meeting.isTranscriptionSupported.returns(true);
906
+ const languageCode = 'fr';
907
+
908
+ meeting.setCaptionLanguage(languageCode).then((resolvedLanguageCode) => {
909
+ assert.calledWith(webex.internal.voicea.requestLanguage, languageCode);
910
+ assert.equal(resolvedLanguageCode, languageCode);
911
+ assert.equal(
912
+ meeting.transcription.languageOptions.currentCaptionLanguage,
913
+ languageCode
914
+ );
915
+ done();
916
+ });
917
+
918
+ assert.calledOnceWithMatch(
919
+ webex.internal.voicea.on,
920
+ VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE
921
+ );
922
+
923
+ // Trigger the event
924
+ const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1];
925
+ voiceaListenerLangugeUpdate({statusCode: 200, languageCode});
926
+ });
927
+
928
+ it('should reject if the statusCode in payload is not 200', (done) => {
929
+ meeting.isTranscriptionSupported.returns(true);
930
+ const languageCode = 'fr';
931
+ const rejectPayload = {
932
+ statusCode: 400,
933
+ message: 'some error message',
934
+ };
935
+
936
+ meeting.setCaptionLanguage(languageCode).catch((payload) => {
937
+ assert.equal(payload, rejectPayload);
938
+ done();
939
+ });
940
+
941
+ assert.calledOnceWithMatch(
942
+ webex.internal.voicea.on,
943
+ VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE
944
+ );
945
+
946
+ // Trigger the event
947
+ const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1];
948
+ voiceaListenerLangugeUpdate(rejectPayload);
949
+ });
950
+ });
951
+
952
+ describe('#setSpokenLanguage', () => {
953
+ beforeEach(() => {
954
+ meeting.isTranscriptionSupported = sinon.stub();
955
+ meeting.transcription = {languageOptions: {}};
956
+ webex.internal.voicea.on = sinon.stub();
957
+ webex.internal.voicea.off = sinon.stub();
958
+ webex.internal.voicea.setSpokenLanguage = sinon.stub();
959
+ });
960
+
961
+ afterEach(() => {
962
+ // Restore the original methods after each test
963
+ sinon.restore();
964
+ });
965
+
966
+ it('should reject if transcription is not supported', (done) => {
967
+ meeting.isTranscriptionSupported.returns(false);
968
+
969
+ meeting.setSpokenLanguage('fr').catch((error) => {
970
+ assert.equal(error.message, 'Webex Assistant is not enabled/supported');
971
+ done();
972
+ });
973
+ });
974
+
975
+ it('should resolve with the language code on successful language update', (done) => {
976
+ meeting.isTranscriptionSupported.returns(true);
977
+ const languageCode = 'fr';
978
+
979
+ meeting.setSpokenLanguage(languageCode).then((resolvedLanguageCode) => {
980
+ assert.calledWith(webex.internal.voicea.setSpokenLanguage, languageCode);
981
+ assert.equal(resolvedLanguageCode, languageCode);
982
+ assert.equal(meeting.transcription.languageOptions.currentSpokenLanguage, languageCode);
983
+ done();
984
+ });
985
+
986
+ assert.calledOnceWithMatch(webex.internal.voicea.on, VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE);
987
+
988
+ // Trigger the event
989
+ const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1];
990
+ voiceaListenerLangugeUpdate({languageCode});
991
+ });
992
+
993
+ it('should reject if the language code does not exist in payload', (done) => {
994
+ meeting.isTranscriptionSupported.returns(true);
995
+ const languageCode = 'fr';
996
+ const rejectPayload = {
997
+ message: 'some error message',
684
998
  };
685
999
 
686
- meeting.stopReceivingTranscription();
687
- assert.calledOnce(meeting.transcription.closeSocket);
1000
+ meeting.setSpokenLanguage(languageCode).catch((payload) => {
1001
+ assert.equal(payload, rejectPayload);
1002
+ done();
1003
+ });
1004
+
1005
+ assert.calledOnceWithMatch(webex.internal.voicea.on, VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE);
1006
+
1007
+ // Trigger the event
1008
+ const voiceaListenerLangugeUpdate = webex.internal.voicea.on.getCall(0).args[1];
1009
+ voiceaListenerLangugeUpdate(rejectPayload);
688
1010
  });
689
1011
  });
1012
+
1013
+ describe('transcription events', () => {
1014
+ beforeEach(() => {
1015
+ meeting.trigger = sinon.stub();
1016
+ });
1017
+
1018
+ it('should trigger meeting:caption-received event', () => {
1019
+ meeting.voiceaListenerCallbacks[VOICEAEVENTS.NEW_CAPTION]({});
1020
+ assert.calledWith(
1021
+ meeting.trigger,
1022
+ EVENT_TRIGGERS.MEETING_CAPTION_RECEIVED
1023
+ );
1024
+ });
1025
+
1026
+ it('should trigger meeting:receiveTranscription:started event', () => {
1027
+ meeting.voiceaListenerCallbacks[VOICEAEVENTS.VOICEA_ANNOUNCEMENT]({});
1028
+ assert.calledWith(
1029
+ meeting.trigger,
1030
+ EVENT_TRIGGERS.MEETING_STARTED_RECEIVING_TRANSCRIPTION
1031
+ );
1032
+ });
1033
+
1034
+ it('should trigger meeting:caption-received event', () => {
1035
+ meeting.voiceaListenerCallbacks[VOICEAEVENTS.NEW_CAPTION]({});
1036
+ assert.calledWith(
1037
+ meeting.trigger,
1038
+ EVENT_TRIGGERS.MEETING_CAPTION_RECEIVED
1039
+ );
1040
+ });
1041
+ });
1042
+
690
1043
  describe('#isReactionsSupported', () => {
691
1044
  it('should return false if the feature is not supported for the meeting', () => {
692
1045
  meeting.locusInfo.controls = {reactions: {enabled: false}};
@@ -851,7 +1204,7 @@ describe('plugin-meetings', () => {
851
1204
  setCorrelationIdSpy = sinon.spy(meeting, 'setCorrelationId');
852
1205
  meeting.setLocus = sinon.stub().returns(true);
853
1206
  webex.meetings.registered = true;
854
- meeting.updateLLMConnection = sinon.stub();
1207
+ meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
855
1208
  });
856
1209
 
857
1210
  describe('successful', () => {
@@ -861,6 +1214,7 @@ describe('plugin-meetings', () => {
861
1214
 
862
1215
  it('should join the meeting and return promise', async () => {
863
1216
  const join = meeting.join({pstnAudioType: 'dial-in'});
1217
+ meeting.config.enableAutomaticLLM = true;
864
1218
 
865
1219
  assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
866
1220
  name: 'client.call.initiated',
@@ -878,6 +1232,16 @@ describe('plugin-meetings', () => {
878
1232
  assert.calledOnce(MeetingUtil.joinMeeting);
879
1233
  assert.calledOnce(meeting.setLocus);
880
1234
  assert.equal(result, joinMeetingResult);
1235
+
1236
+ assert.calledWith(
1237
+ TriggerProxy.trigger,
1238
+ sinon.match.instanceOf(Meeting),
1239
+ {
1240
+ file: 'meeting/index',
1241
+ function: 'join',
1242
+ },
1243
+ EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED
1244
+ );
881
1245
  });
882
1246
 
883
1247
  it('should take trigger from meeting joinTrigger if available', () => {
@@ -953,6 +1317,7 @@ describe('plugin-meetings', () => {
953
1317
  });
954
1318
 
955
1319
  it('should post error event if failed', async () => {
1320
+ MeetingUtil.isPinOrGuest = sinon.stub().returns(false);
956
1321
  await meeting.join().catch(() => {
957
1322
  assert.deepEqual(
958
1323
  webex.internal.newMetrics.submitClientEvent.getCall(1).args[0].name,
@@ -1056,60 +1421,6 @@ describe('plugin-meetings', () => {
1056
1421
  });
1057
1422
  });
1058
1423
 
1059
- describe('receive transcription', () => {
1060
- it('should invoke `receiveTranscription()` if receiveTranscription is set to true', async () => {
1061
- meeting.isTranscriptionSupported = sinon.stub().returns(true);
1062
- meeting.receiveTranscription = sinon.stub().returns(Promise.resolve());
1063
-
1064
- await meeting.join({receiveTranscription: true});
1065
- assert.calledOnce(meeting.receiveTranscription);
1066
- });
1067
-
1068
- it('make sure that join does not wait for setting up receive transcriptions', async () => {
1069
- const defer = new Defer();
1070
-
1071
- meeting.isTranscriptionSupported = sinon.stub().returns(true);
1072
- meeting.receiveTranscription = sinon.stub().returns(defer.promise);
1073
-
1074
- const result = await meeting.join({receiveTranscription: true});
1075
-
1076
- assert.equal(result, joinMeetingResult);
1077
-
1078
- defer.resolve();
1079
- });
1080
-
1081
- it('handles catching error of receiveTranscription(), and join still resolves', async () => {
1082
- const defer = new Defer();
1083
-
1084
- meeting.isTranscriptionSupported = sinon.stub().returns(true);
1085
- meeting.receiveTranscription = sinon.stub().returns(defer.promise);
1086
-
1087
- const result = await meeting.join({receiveTranscription: true});
1088
-
1089
- assert.equal(result, joinMeetingResult);
1090
-
1091
- defer.reject(new Error('bad day', {cause: 'bad weather'}));
1092
-
1093
- try {
1094
- await defer.promise;
1095
- } catch (err) {
1096
- assert.deepEqual(Metrics.sendBehavioralMetric.getCalls()[0].args, [
1097
- BEHAVIORAL_METRICS.JOIN_SUCCESS,
1098
- {correlation_id: meeting.correlationId},
1099
- ]);
1100
-
1101
- assert.deepEqual(Metrics.sendBehavioralMetric.getCalls()[1].args, [
1102
- BEHAVIORAL_METRICS.RECEIVE_TRANSCRIPTION_AFTER_JOIN_FAILURE,
1103
- {
1104
- correlation_id: meeting.correlationId,
1105
- reason: err.message,
1106
- stack: err.stack,
1107
- },
1108
- ]);
1109
- }
1110
- });
1111
- });
1112
-
1113
1424
  describe('refreshPermissionToken', () => {
1114
1425
  it('should continue if permissionTokenRefresh fails with a generic error', async () => {
1115
1426
  meeting.checkAndRefreshPermissionToken = sinon.stub().rejects(new Error('bad day'));
@@ -2300,6 +2611,7 @@ describe('plugin-meetings', () => {
2300
2611
 
2301
2612
  beforeEach(async () => {
2302
2613
  meeting.meetingState = 'ACTIVE';
2614
+ meeting.remoteShareInstanceId = '1234';
2303
2615
  prevConfigValue = meeting.config.stats.enableStatsAnalyzer;
2304
2616
 
2305
2617
  meeting.config.stats.enableStatsAnalyzer = true;
@@ -2405,6 +2717,66 @@ describe('plugin-meetings', () => {
2405
2717
  });
2406
2718
  });
2407
2719
 
2720
+ it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics for share', async () => {
2721
+ statsAnalyzerStub.emit(
2722
+ {file: 'test', function: 'test'},
2723
+ StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STARTED,
2724
+ {type: 'share'}
2725
+ );
2726
+
2727
+ assert.calledWith(
2728
+ TriggerProxy.trigger,
2729
+ sinon.match.instanceOf(Meeting),
2730
+ {
2731
+ file: 'meeting/index',
2732
+ function: 'addMedia',
2733
+ },
2734
+ EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
2735
+ {
2736
+ type: 'share',
2737
+ }
2738
+ );
2739
+ assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
2740
+ name: 'client.media.rx.start',
2741
+ payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
2742
+ options: {
2743
+ meetingId: meeting.id,
2744
+ },
2745
+ });
2746
+
2747
+ assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
2748
+ name: 'client.media.render.start',
2749
+ payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
2750
+ options: {
2751
+ meetingId: meeting.id,
2752
+ },
2753
+ });
2754
+ });
2755
+
2756
+ it('REMOTE_MEDIA_STOPPED triggers the right metrics for share', async () => {
2757
+ statsAnalyzerStub.emit(
2758
+ {file: 'test', function: 'test'},
2759
+ StatsAnalyzerModule.EVENTS.REMOTE_MEDIA_STOPPED,
2760
+ {type: 'share'}
2761
+ );
2762
+
2763
+ assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
2764
+ name: 'client.media.rx.stop',
2765
+ payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
2766
+ options: {
2767
+ meetingId: meeting.id,
2768
+ },
2769
+ });
2770
+
2771
+ assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
2772
+ name: 'client.media.render.stop',
2773
+ payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
2774
+ options: {
2775
+ meetingId: meeting.id,
2776
+ },
2777
+ });
2778
+ });
2779
+
2408
2780
  it('calls submitMQE correctly', async () => {
2409
2781
  const fakeData = {intervalMetadata: {bla: 'bla'}};
2410
2782
 
@@ -2649,9 +3021,10 @@ describe('plugin-meetings', () => {
2649
3021
  meeting.setMercuryListener = sinon.stub();
2650
3022
  meeting.locusInfo.onFullLocus = sinon.stub();
2651
3023
  meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
2652
- meeting.roap.doTurnDiscovery = sinon
2653
- .stub()
2654
- .resolves({turnServerInfo: {}, turnDiscoverySkippedReason: 'reachability'});
3024
+ meeting.roap.doTurnDiscovery = sinon.stub().resolves({
3025
+ turnServerInfo: {url: 'turn-url', username: 'turn user', password: 'turn password'},
3026
+ turnDiscoverySkippedReason: 'reachability',
3027
+ });
2655
3028
  meeting.deferSDPAnswer = new Defer();
2656
3029
  meeting.deferSDPAnswer.resolve();
2657
3030
  meeting.webex.meetings.meetingCollection = new MeetingCollection();
@@ -2662,7 +3035,7 @@ describe('plugin-meetings', () => {
2662
3035
  // setup things that are expected to be the same across all the tests and are actually irrelevant for these tests
2663
3036
  expectedDebugId = `MC-${meeting.id.substring(0, 4)}`;
2664
3037
  expectedMediaConnectionConfig = {
2665
- iceServers: [{urls: undefined, username: '', credential: ''}],
3038
+ iceServers: [{urls: 'turn-url', username: 'turn user', credential: 'turn password'}],
2666
3039
  skipInactiveTransceivers: false,
2667
3040
  requireH264: true,
2668
3041
  sdpMunging: {
@@ -2927,28 +3300,52 @@ describe('plugin-meetings', () => {
2927
3300
  if (stream !== undefined) {
2928
3301
  switch (type) {
2929
3302
  case 'audio':
2930
- assert.calledOnceWithExactly(
2931
- meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream,
2932
- stream
2933
- );
3303
+ if (stream?.readyState === 'ended') {
3304
+ assert.notCalled(
3305
+ meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream
3306
+ );
3307
+ } else {
3308
+ assert.calledOnceWithExactly(
3309
+ meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream,
3310
+ stream
3311
+ );
3312
+ }
2934
3313
  break;
2935
3314
  case 'video':
2936
- assert.calledOnceWithExactly(
2937
- meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream,
2938
- stream
2939
- );
3315
+ if (stream?.readyState === 'ended') {
3316
+ assert.notCalled(
3317
+ meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream
3318
+ );
3319
+ } else {
3320
+ assert.calledOnceWithExactly(
3321
+ meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream,
3322
+ stream
3323
+ );
3324
+ }
2940
3325
  break;
2941
3326
  case 'screenShareAudio':
2942
- assert.calledOnceWithExactly(
2943
- meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream,
2944
- stream
2945
- );
3327
+ if (stream?.readyState === 'ended') {
3328
+ assert.notCalled(
3329
+ meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream
3330
+ );
3331
+ } else {
3332
+ assert.calledOnceWithExactly(
3333
+ meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream,
3334
+ stream
3335
+ );
3336
+ }
2946
3337
  break;
2947
3338
  case 'screenShareVideo':
2948
- assert.calledOnceWithExactly(
2949
- meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream,
2950
- stream
2951
- );
3339
+ if (stream?.readyState === 'ended') {
3340
+ assert.notCalled(
3341
+ meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream
3342
+ );
3343
+ } else {
3344
+ assert.calledOnceWithExactly(
3345
+ meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream,
3346
+ stream
3347
+ );
3348
+ }
2952
3349
  break;
2953
3350
  }
2954
3351
  }
@@ -3010,6 +3407,7 @@ describe('plugin-meetings', () => {
3010
3407
  });
3011
3408
 
3012
3409
  it('addMedia() works correctly when media is enabled with streams to publish', async () => {
3410
+ const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
3013
3411
  await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
3014
3412
  await simulateRoapOffer();
3015
3413
  await simulateRoapOk();
@@ -3040,9 +3438,12 @@ describe('plugin-meetings', () => {
3040
3438
 
3041
3439
  // and that these were the only /media requests that were sent
3042
3440
  assert.calledTwice(locusMediaRequestStub);
3441
+
3442
+ assert.calledOnce(handleDeviceLoggingSpy);
3043
3443
  });
3044
3444
 
3045
3445
  it('addMedia() works correctly when media is enabled with streams to publish and stream is user muted', async () => {
3446
+ const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
3046
3447
  fakeMicrophoneStream.userMuted = true;
3047
3448
 
3048
3449
  await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
@@ -3072,6 +3473,41 @@ describe('plugin-meetings', () => {
3072
3473
  // check OK was sent with the right audioMuted/videoMuted values
3073
3474
  checkOkSent({audioMuted: true, videoMuted: true});
3074
3475
 
3476
+ // and that these were the only /media requests that were sent
3477
+ assert.calledTwice(locusMediaRequestStub);
3478
+ assert.calledOnce(handleDeviceLoggingSpy);
3479
+ });
3480
+
3481
+ it('addMedia() works correctly when media is enabled with tracks to publish and track is ended', async () => {
3482
+ fakeMicrophoneStream.readyState = 'ended';
3483
+
3484
+ await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
3485
+ await simulateRoapOffer();
3486
+ await simulateRoapOk();
3487
+
3488
+ // check RoapMediaConnection was created correctly
3489
+ checkMediaConnectionCreated({
3490
+ mediaConnectionConfig: expectedMediaConnectionConfig,
3491
+ localStreams: {
3492
+ audio: undefined,
3493
+ video: undefined,
3494
+ screenShareVideo: undefined,
3495
+ screenShareAudio: undefined,
3496
+ },
3497
+ direction: {
3498
+ audio: 'sendrecv',
3499
+ video: 'sendrecv',
3500
+ screenShare: 'recvonly',
3501
+ },
3502
+ remoteQualityLevel: 'HIGH',
3503
+ expectedDebugId,
3504
+ meetingId: meeting.id,
3505
+ });
3506
+ // and SDP offer was sent with the right audioMuted/videoMuted values
3507
+ checkSdpOfferSent({audioMuted: true, videoMuted: true});
3508
+ // check OK was sent with the right audioMuted/videoMuted values
3509
+ checkOkSent({audioMuted: true, videoMuted: true});
3510
+
3075
3511
  // and that these were the only /media requests that were sent
3076
3512
  assert.calledTwice(locusMediaRequestStub);
3077
3513
  });
@@ -3111,6 +3547,7 @@ describe('plugin-meetings', () => {
3111
3547
  });
3112
3548
 
3113
3549
  it('addMedia() works correctly when media is disabled with streams to publish', async () => {
3550
+ const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
3114
3551
  await meeting.addMedia({
3115
3552
  localStreams: {microphone: fakeMicrophoneStream},
3116
3553
  audioEnabled: false,
@@ -3144,8 +3581,22 @@ describe('plugin-meetings', () => {
3144
3581
 
3145
3582
  // and that these were the only /media requests that were sent
3146
3583
  assert.calledTwice(locusMediaRequestStub);
3584
+ assert.calledOnce(handleDeviceLoggingSpy);
3147
3585
  });
3148
3586
 
3587
+ it('handleDeviceLogging not called when media is disabled', async () => {
3588
+ const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
3589
+ await meeting.addMedia({
3590
+ localStreams: {microphone: fakeMicrophoneStream},
3591
+ audioEnabled: false,
3592
+ videoEnabled: false
3593
+ });
3594
+ await simulateRoapOffer();
3595
+ await simulateRoapOk();
3596
+
3597
+ assert.notCalled(handleDeviceLoggingSpy);
3598
+ })
3599
+
3149
3600
  it('addMedia() works correctly when media is disabled with no streams to publish', async () => {
3150
3601
  await meeting.addMedia({audioEnabled: false});
3151
3602
  await simulateRoapOffer();
@@ -3716,6 +4167,7 @@ describe('plugin-meetings', () => {
3716
4167
  meeting.unsetPeerConnections = sinon.stub().returns(true);
3717
4168
  meeting.logger.error = sinon.stub().returns(true);
3718
4169
  meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
4170
+ webex.internal.voicea.off = sinon.stub().returns(true);
3719
4171
 
3720
4172
  // A meeting needs to be joined to leave
3721
4173
  meeting.meetingState = 'ACTIVE';
@@ -4518,6 +4970,8 @@ describe('plugin-meetings', () => {
4518
4970
  sipUrl: 'some_sip_url', // or sipMeetingUri
4519
4971
  meetingNumber: '123456', // this.config.experimental.enableUnifiedMeetings
4520
4972
  hostId: 'some_host_id', // this.owner;
4973
+ permissionToken:
4974
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtb2NrUGFzc3dvcmQiOiJ0aGlzSXNNb2NrUGFzc3dvcmQifQ.3-WXiR8vhGUH3VXO0DTpsTwnnkVQ3vhGQcktwIarj3I',
4521
4975
  };
4522
4976
  const FAKE_MEETING_INFO_LOOKUP_URL = 'meetingLookupUrl';
4523
4977
 
@@ -4942,6 +5396,8 @@ describe('plugin-meetings', () => {
4942
5396
  sipUrl: 'some_sip_url', // or sipMeetingUri
4943
5397
  meetingNumber: '123456', // this.config.experimental.enableUnifiedMeetings
4944
5398
  hostId: 'some_host_id', // this.owner;
5399
+ permissionToken:
5400
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
4945
5401
  };
4946
5402
 
4947
5403
  [
@@ -5094,6 +5550,8 @@ describe('plugin-meetings', () => {
5094
5550
  sipUrl: 'some_sip_url',
5095
5551
  meetingNumber: '123456',
5096
5552
  hostId: 'some_host_id',
5553
+ permissionToken:
5554
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
5097
5555
  };
5098
5556
  const FAKE_MEETING_INFO_LOOKUP_URL = 'meetingLookupUrl';
5099
5557
  const FAKE_PERMISSION_TOKEN = {someField: 'some value'};
@@ -5540,6 +5998,8 @@ describe('plugin-meetings', () => {
5540
5998
  meeting.unsetPeerConnections = sinon.stub().returns(true);
5541
5999
  meeting.logger.error = sinon.stub().returns(true);
5542
6000
  meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
6001
+ meeting.transcription = {};
6002
+ meeting.stopTranscription = sinon.stub();
5543
6003
 
5544
6004
  // A meeting needs to be joined to end
5545
6005
  meeting.meetingState = 'ACTIVE';
@@ -5560,6 +6020,7 @@ describe('plugin-meetings', () => {
5560
6020
  assert.calledOnce(meeting?.closePeerConnections);
5561
6021
  assert.calledOnce(meeting?.unsetRemoteStreams);
5562
6022
  assert.calledOnce(meeting?.unsetPeerConnections);
6023
+ assert.calledOnce(meeting?.stopTranscription);
5563
6024
  });
5564
6025
  });
5565
6026
 
@@ -6008,6 +6469,65 @@ describe('plugin-meetings', () => {
6008
6469
  checkScreenShareVideoPublished(videoShareStream);
6009
6470
  checkScreenShareAudioPublished(audioShareStream);
6010
6471
  });
6472
+
6473
+ [
6474
+ {
6475
+ endedStream: 'microphone',
6476
+ streams: {
6477
+ microphone: {
6478
+ readyState: 'ended',
6479
+ },
6480
+ camera: undefined,
6481
+ screenShare: {
6482
+ audio: undefined,
6483
+ video: undefined,
6484
+ },
6485
+ },
6486
+ },
6487
+ {
6488
+ endedStream: 'camera',
6489
+ streams: {
6490
+ microphone: undefined,
6491
+ camera: {
6492
+ readyState: 'ended',
6493
+ },
6494
+ screenShare: {
6495
+ audio: undefined,
6496
+ video: undefined,
6497
+ },
6498
+ },
6499
+ },
6500
+ {
6501
+ endedStream: 'screenShare audio',
6502
+ streams: {
6503
+ microphone: undefined,
6504
+ camera: undefined,
6505
+ screenShare: {
6506
+ audio: {
6507
+ readyState: 'ended',
6508
+ },
6509
+ video: undefined,
6510
+ },
6511
+ },
6512
+ },
6513
+ {
6514
+ endedStream: 'screenShare video',
6515
+ streams: {
6516
+ microphone: undefined,
6517
+ camera: undefined,
6518
+ screenShare: {
6519
+ audio: undefined,
6520
+ video: {
6521
+ readyState: 'ended',
6522
+ },
6523
+ },
6524
+ },
6525
+ },
6526
+ ].forEach(({endedStream, streams}) => {
6527
+ it(`throws error if readyState of ${endedStream} is ended`, async () => {
6528
+ assert.isRejected(meeting.publishStreams(streams));
6529
+ });
6530
+ });
6011
6531
  });
6012
6532
 
6013
6533
  describe('unpublishStreams', () => {
@@ -6597,6 +7117,7 @@ describe('plugin-meetings', () => {
6597
7117
 
6598
7118
  describe('CONNECTION_STATE_CHANGED event when state = "Disconnected"', () => {
6599
7119
  beforeEach(() => {
7120
+ Metrics.sendBehavioralMetric = sinon.stub();
6600
7121
  meeting.reconnectionManager = new ReconnectionManager(meeting);
6601
7122
  meeting.reconnectionManager.iceReconnected = sinon.stub().returns(undefined);
6602
7123
  meeting.setNetworkStatus = sinon.stub().returns(undefined);
@@ -6631,7 +7152,6 @@ describe('plugin-meetings', () => {
6631
7152
  meeting.reconnectionManager.waitForIceReconnect = sinon.stub().resolves();
6632
7153
 
6633
7154
  mockDisconnectedEvent();
6634
-
6635
7155
  await testUtils.flushPromises();
6636
7156
 
6637
7157
  assert.calledOnce(meeting.setNetworkStatus);
@@ -7232,6 +7752,7 @@ describe('plugin-meetings', () => {
7232
7752
  });
7233
7753
  it('listens to the self admitted guest event', (done) => {
7234
7754
  meeting.stopKeepAlive = sinon.stub();
7755
+ meeting.updateLLMConnection = sinon.stub();
7235
7756
  meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ADMITTED_GUEST', test1);
7236
7757
  assert.calledOnceWithExactly(meeting.stopKeepAlive);
7237
7758
  assert.calledThrice(TriggerProxy.trigger);
@@ -7242,6 +7763,7 @@ describe('plugin-meetings', () => {
7242
7763
  'meeting:self:guestAdmitted',
7243
7764
  {payload: test1}
7244
7765
  );
7766
+ assert.calledOnce(meeting.updateLLMConnection);
7245
7767
  done();
7246
7768
  });
7247
7769
 
@@ -7748,10 +8270,11 @@ describe('plugin-meetings', () => {
7748
8270
  });
7749
8271
 
7750
8272
  describe('#setUpLocusInfoMeetingListener', () => {
8273
+ let cleanUpSpy;
7751
8274
  it('listens to destroy meeting event from locus info ', (done) => {
7752
8275
  TriggerProxy.trigger.reset();
7753
8276
  sinon.stub(meeting.reconnectionManager, 'cleanUp');
7754
- sinon.spy(MeetingUtil, 'cleanUp');
8277
+ cleanUpSpy = sinon.stub(MeetingUtil, 'cleanUp');
7755
8278
 
7756
8279
  meeting.locusInfo.emit({function: 'test', file: 'test'}, EVENTS.DESTROY_MEETING, {
7757
8280
  shouldLeave: false,
@@ -7772,6 +8295,7 @@ describe('plugin-meetings', () => {
7772
8295
  meetingId: meeting.id,
7773
8296
  }
7774
8297
  );
8298
+ cleanUpSpy.restore();
7775
8299
  done();
7776
8300
  });
7777
8301
  });
@@ -7885,13 +8409,12 @@ describe('plugin-meetings', () => {
7885
8409
  it('sets correctly', () => {
7886
8410
  assert.notOk(meeting.permissionTokenPayload);
7887
8411
 
7888
- const permissionTokenPayloadData = {permission: {userPolicies: {a: true}}, exp: '1234'};
7889
-
7890
- const jwtDecodeStub = sinon.stub(jwt, 'decode').returns(permissionTokenPayloadData);
8412
+ const permissionTokenPayloadData = {permission: {userPolicies: {a: true}}, exp: '123456'};
7891
8413
 
7892
- meeting.setPermissionTokenPayload();
8414
+ meeting.setPermissionTokenPayload(
8415
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0'
8416
+ );
7893
8417
 
7894
- assert.calledOnce(jwtDecodeStub);
7895
8418
  assert.deepEqual(meeting.permissionTokenPayload, permissionTokenPayloadData);
7896
8419
  assert.deepEqual(meeting.permissionTokenReceivedLocalTime, now);
7897
8420
  });
@@ -8079,7 +8602,8 @@ describe('plugin-meetings', () => {
8079
8602
  locusUrl: url1,
8080
8603
  meetingJoinUrl: url2,
8081
8604
  meetingNumber: '12345',
8082
- permissionToken: 'abc',
8605
+ permissionToken:
8606
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
8083
8607
  sipMeetingUri: test1,
8084
8608
  sipUrl: test1,
8085
8609
  owner: test2,
@@ -8092,8 +8616,10 @@ describe('plugin-meetings', () => {
8092
8616
  sipUri: 'locusSipUri',
8093
8617
  meetingNumber: 'locusMeetingId',
8094
8618
  meetingJoinUrl: url2,
8619
+ permissionToken:
8620
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
8095
8621
  owner: 'locusOwner',
8096
- permissionToken: 'abc',
8622
+ selfUserPolicies: {a: true},
8097
8623
  };
8098
8624
 
8099
8625
  checkParseMeetingInfo(expectedInfoToParse);
@@ -8106,7 +8632,8 @@ describe('plugin-meetings', () => {
8106
8632
  locusUrl: url1,
8107
8633
  meetingJoinUrl: url2,
8108
8634
  meetingNumber: '12345',
8109
- permissionToken: 'abc',
8635
+ permissionToken:
8636
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
8110
8637
  sipMeetingUri: test1,
8111
8638
  sipUrl: test1,
8112
8639
  owner: test2,
@@ -8119,8 +8646,10 @@ describe('plugin-meetings', () => {
8119
8646
  sipUri: test1,
8120
8647
  meetingNumber: '12345',
8121
8648
  meetingJoinUrl: url2,
8649
+ permissionToken:
8650
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
8122
8651
  owner: test2,
8123
- permissionToken: 'abc',
8652
+ selfUserPolicies: {a: true},
8124
8653
  };
8125
8654
 
8126
8655
  checkParseMeetingInfo(expectedInfoToParse);
@@ -8134,7 +8663,8 @@ describe('plugin-meetings', () => {
8134
8663
  locusUrl: url1,
8135
8664
  meetingJoinUrl: url2,
8136
8665
  meetingNumber: '12345',
8137
- permissionToken: 'abc',
8666
+ permissionToken:
8667
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
8138
8668
  sipMeetingUri: test1,
8139
8669
  sipUrl: test1,
8140
8670
  owner: test2,
@@ -8148,11 +8678,41 @@ describe('plugin-meetings', () => {
8148
8678
  meetingNumber: '12345',
8149
8679
  meetingJoinUrl: url2,
8150
8680
  owner: test2,
8151
- permissionToken: 'abc',
8681
+ permissionToken:
8682
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
8683
+ selfUserPolicies: {a: true},
8152
8684
  };
8153
8685
 
8154
8686
  checkParseMeetingInfo(expectedInfoToParse);
8155
8687
  });
8688
+
8689
+ it('should parse meeting info, set values, and return null when permissionToken is not present', () => {
8690
+ meeting.config.experimental = {enableMediaNegotiatedEvent: true};
8691
+ meeting.config.experimental.enableUnifiedMeetings = true;
8692
+ const FAKE_STRING_DESTINATION = 'sipUrl';
8693
+ const FAKE_MEETING_INFO = {
8694
+ conversationUrl: uuid1,
8695
+ locusUrl: url1,
8696
+ meetingJoinUrl: url2,
8697
+ meetingNumber: '12345',
8698
+ sipMeetingUri: test1,
8699
+ sipUrl: test1,
8700
+ owner: test2,
8701
+ };
8702
+
8703
+ meeting.parseMeetingInfo(FAKE_MEETING_INFO, FAKE_STRING_DESTINATION);
8704
+ const expectedInfoToParse = {
8705
+ conversationUrl: uuid1,
8706
+ locusUrl: url1,
8707
+ sipUri: test1,
8708
+ meetingNumber: '12345',
8709
+ meetingJoinUrl: url2,
8710
+ owner: test2,
8711
+ };
8712
+
8713
+ checkParseMeetingInfo(expectedInfoToParse);
8714
+ });
8715
+
8156
8716
  it('should parse interpretation info correctly', () => {
8157
8717
  const parseInterpretationInfo = sinon.spy(MeetingUtil, 'parseInterpretationInfo');
8158
8718
  const mockToggleOnData = {
@@ -8169,6 +8729,8 @@ describe('plugin-meetings', () => {
8169
8729
  },
8170
8730
  ],
8171
8731
  },
8732
+ permissionToken:
8733
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
8172
8734
  };
8173
8735
  meeting.parseMeetingInfo(mockToggleOnData);
8174
8736
  assert.calledOnceWithExactly(parseInterpretationInfo, meeting, mockToggleOnData);
@@ -8872,7 +9434,7 @@ describe('plugin-meetings', () => {
8872
9434
  // Due to import tree issues, hasHints must be stubed within the scope of the `it`.
8873
9435
  const restorableHasHints = ControlsOptionsUtil.hasHints;
8874
9436
  ControlsOptionsUtil.hasHints = sinon.stub().returns(true);
8875
- ControlsOptionsUtil.hasPolicies = sinon.stub().returns(true);
9437
+ const hasPoliciesSpy = sinon.stub(ControlsOptionsUtil, 'hasPolicies').returns(true);
8876
9438
 
8877
9439
  const selfUserPolicies = {a: true};
8878
9440
  meeting.selfUserPolicies = {a: true};
@@ -9013,6 +9575,7 @@ describe('plugin-meetings', () => {
9013
9575
  assert.notCalled(TriggerProxy.trigger);
9014
9576
 
9015
9577
  ControlsOptionsUtil.hasHints = restorableHasHints;
9578
+ hasPoliciesSpy.restore();
9016
9579
  });
9017
9580
  });
9018
9581
 
@@ -9286,9 +9849,16 @@ describe('plugin-meetings', () => {
9286
9849
  it('check triggerAnnotationInfoEvent event', () => {
9287
9850
  TriggerProxy.trigger.reset();
9288
9851
  const annotationInfo = {version: '1', policy: 'Approval'};
9289
- const expectAnnotationInfo = {annotationInfo, meetingId: meeting.id};
9852
+ const expectAnnotationInfo = {
9853
+ annotationInfo,
9854
+ meetingId: meeting.id,
9855
+ resourceType: 'FILE',
9856
+ };
9290
9857
  meeting.webex.meetings = {};
9291
- meeting.triggerAnnotationInfoEvent({annotation: annotationInfo}, {});
9858
+ meeting.triggerAnnotationInfoEvent(
9859
+ {annotation: annotationInfo, resourceType: 'FILE'},
9860
+ {}
9861
+ );
9292
9862
  assert.calledWith(
9293
9863
  TriggerProxy.trigger,
9294
9864
  {},
@@ -9302,8 +9872,8 @@ describe('plugin-meetings', () => {
9302
9872
 
9303
9873
  TriggerProxy.trigger.reset();
9304
9874
  meeting.triggerAnnotationInfoEvent(
9305
- {annotation: annotationInfo},
9306
- {annotation: annotationInfo}
9875
+ {annotation: annotationInfo, resourceType: 'FILE'},
9876
+ {annotation: annotationInfo, resourceType: 'FILE'}
9307
9877
  );
9308
9878
  assert.notCalled(TriggerProxy.trigger);
9309
9879
 
@@ -9312,10 +9882,11 @@ describe('plugin-meetings', () => {
9312
9882
  const expectAnnotationInfoUpdated = {
9313
9883
  annotationInfo: annotationInfoUpdate,
9314
9884
  meetingId: meeting.id,
9885
+ resourceType: 'FILE',
9315
9886
  };
9316
9887
  meeting.triggerAnnotationInfoEvent(
9317
- {annotation: annotationInfoUpdate},
9318
- {annotation: annotationInfo}
9888
+ {annotation: annotationInfoUpdate, resourceType: 'FILE'},
9889
+ {annotation: annotationInfo, resourceType: 'FILE'}
9319
9890
  );
9320
9891
  assert.calledWith(
9321
9892
  TriggerProxy.trigger,
@@ -9329,7 +9900,10 @@ describe('plugin-meetings', () => {
9329
9900
  );
9330
9901
 
9331
9902
  TriggerProxy.trigger.reset();
9332
- meeting.triggerAnnotationInfoEvent(null, {annotation: annotationInfoUpdate});
9903
+ meeting.triggerAnnotationInfoEvent(null, {
9904
+ annotation: annotationInfoUpdate,
9905
+ resourceType: 'FILE',
9906
+ });
9333
9907
  assert.notCalled(TriggerProxy.trigger);
9334
9908
  });
9335
9909
  });
@@ -9353,6 +9927,11 @@ describe('plugin-meetings', () => {
9353
9927
  'https://board-a.wbx2.com/board/api/v1/channels/977a7330-54f4-11eb-b1ef-91f5eefc7bf3',
9354
9928
  };
9355
9929
 
9930
+ const SHARE_TYPE = {
9931
+ FILE: 'FILE',
9932
+ DESKTOP: 'DESKTOP',
9933
+ };
9934
+
9356
9935
  const DEVICE_URL = {
9357
9936
  LOCAL_WEB: 'my-web-url',
9358
9937
  LOCAL_MAC: 'my-mac-url',
@@ -9364,11 +9943,14 @@ describe('plugin-meetings', () => {
9364
9943
  beneficiaryId = null,
9365
9944
  disposition = null,
9366
9945
  deviceUrlSharing = null,
9367
- annotation = undefined
9946
+ annotation = undefined,
9947
+ resourceType = undefined
9368
9948
  ) => ({
9369
9949
  beneficiaryId,
9370
9950
  disposition,
9371
9951
  deviceUrlSharing,
9952
+ annotation,
9953
+ resourceType,
9372
9954
  });
9373
9955
  const generateWhiteboard = (
9374
9956
  beneficiaryId = null,
@@ -9387,7 +9969,8 @@ describe('plugin-meetings', () => {
9387
9969
  annotation,
9388
9970
  url,
9389
9971
  shareInstanceId,
9390
- deviceUrlSharing
9972
+ deviceUrlSharing,
9973
+ resourceType
9391
9974
  ) => {
9392
9975
  const newPayload = cloneDeep(payload);
9393
9976
 
@@ -9421,7 +10004,8 @@ describe('plugin-meetings', () => {
9421
10004
  beneficiaryId,
9422
10005
  FLOOR_ACTION.GRANTED,
9423
10006
  deviceUrlSharing,
9424
- annotation
10007
+ annotation,
10008
+ resourceType
9425
10009
  );
9426
10010
 
9427
10011
  if (isEqual(newPayload.current, newPayload.previous)) {
@@ -9482,6 +10066,7 @@ describe('plugin-meetings', () => {
9482
10066
  url,
9483
10067
  shareInstanceId,
9484
10068
  annotationInfo: undefined,
10069
+ resourceType: undefined,
9485
10070
  },
9486
10071
  });
9487
10072
  }
@@ -10323,7 +10908,8 @@ describe('plugin-meetings', () => {
10323
10908
  undefined,
10324
10909
  undefined,
10325
10910
  undefined,
10326
- DEVICE_URL.REMOTE_A
10911
+ DEVICE_URL.REMOTE_A,
10912
+ undefined
10327
10913
  );
10328
10914
  const data2 = generateData(
10329
10915
  data1.payload,
@@ -10336,9 +10922,39 @@ describe('plugin-meetings', () => {
10336
10922
  undefined,
10337
10923
  undefined,
10338
10924
  undefined,
10339
- DEVICE_URL.REMOTE_B
10925
+ DEVICE_URL.REMOTE_B,
10926
+ undefined
10927
+ );
10928
+ const data3 = generateData(data2.payload, false, true, USER_IDS.REMOTE_B, undefined);
10929
+
10930
+ payloadTestHelper([data1, data2, data3]);
10931
+ });
10932
+ });
10933
+
10934
+ describe('File Share --> Desktop Share', () => {
10935
+ it('Scenario #1: remote person A shares file then share desktop', () => {
10936
+ const data1 = generateData(
10937
+ blankPayload,
10938
+ true,
10939
+ true,
10940
+ USER_IDS.ME,
10941
+ undefined,
10942
+ false,
10943
+ undefined,
10944
+ undefined,
10945
+ undefined,
10946
+ undefined,
10947
+ DEVICE_URL.LOCAL_WEB,
10948
+ SHARE_TYPE.FILE
10949
+ );
10950
+ const data2 = generateData(
10951
+ data1.payload,
10952
+ true,
10953
+ false,
10954
+ USER_IDS.ME,
10955
+ SHARE_TYPE.DESKTOP
10340
10956
  );
10341
- const data3 = generateData(data2.payload, false, true, USER_IDS.REMOTE_B);
10957
+ const data3 = generateData(data2.payload, true, true, USER_IDS.ME);
10342
10958
 
10343
10959
  payloadTestHelper([data1, data2, data3]);
10344
10960
  });