@webex/plugin-meetings 3.8.0-next.5 → 3.8.0-next.51

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 (133) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/config.js +1 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/constants.js +14 -1
  6. package/dist/constants.js.map +1 -1
  7. package/dist/controls-options-manager/enums.js +2 -0
  8. package/dist/controls-options-manager/enums.js.map +1 -1
  9. package/dist/controls-options-manager/types.js.map +1 -1
  10. package/dist/controls-options-manager/util.js +52 -0
  11. package/dist/controls-options-manager/util.js.map +1 -1
  12. package/dist/interpretation/index.js +1 -1
  13. package/dist/interpretation/siLanguage.js +1 -1
  14. package/dist/locus-info/controlsUtils.js +28 -10
  15. package/dist/locus-info/controlsUtils.js.map +1 -1
  16. package/dist/locus-info/index.js +20 -1
  17. package/dist/locus-info/index.js.map +1 -1
  18. package/dist/media/index.js +3 -15
  19. package/dist/media/index.js.map +1 -1
  20. package/dist/meeting/in-meeting-actions.js +11 -1
  21. package/dist/meeting/in-meeting-actions.js.map +1 -1
  22. package/dist/meeting/index.js +544 -324
  23. package/dist/meeting/index.js.map +1 -1
  24. package/dist/meeting/locusMediaRequest.js +26 -23
  25. package/dist/meeting/locusMediaRequest.js.map +1 -1
  26. package/dist/meeting/muteState.js +0 -2
  27. package/dist/meeting/muteState.js.map +1 -1
  28. package/dist/meeting/request.js +30 -0
  29. package/dist/meeting/request.js.map +1 -1
  30. package/dist/meeting/request.type.js.map +1 -1
  31. package/dist/meeting/util.js +27 -2
  32. package/dist/meeting/util.js.map +1 -1
  33. package/dist/meeting-info/meeting-info-v2.js +359 -60
  34. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  35. package/dist/meetings/index.js +69 -1
  36. package/dist/meetings/index.js.map +1 -1
  37. package/dist/meetings/util.js +14 -0
  38. package/dist/meetings/util.js.map +1 -1
  39. package/dist/member/index.js +10 -0
  40. package/dist/member/index.js.map +1 -1
  41. package/dist/member/util.js +3 -0
  42. package/dist/member/util.js.map +1 -1
  43. package/dist/metrics/constants.js +9 -0
  44. package/dist/metrics/constants.js.map +1 -1
  45. package/dist/reachability/clusterReachability.js +63 -27
  46. package/dist/reachability/clusterReachability.js.map +1 -1
  47. package/dist/reachability/index.js +112 -47
  48. package/dist/reachability/index.js.map +1 -1
  49. package/dist/reachability/reachability.types.js +14 -0
  50. package/dist/reachability/reachability.types.js.map +1 -1
  51. package/dist/reachability/request.js +19 -3
  52. package/dist/reachability/request.js.map +1 -1
  53. package/dist/reconnection-manager/index.js +2 -2
  54. package/dist/reconnection-manager/index.js.map +1 -1
  55. package/dist/recording-controller/util.js +5 -5
  56. package/dist/recording-controller/util.js.map +1 -1
  57. package/dist/roap/index.js.map +1 -1
  58. package/dist/roap/turnDiscovery.js +45 -27
  59. package/dist/roap/turnDiscovery.js.map +1 -1
  60. package/dist/roap/types.js +17 -0
  61. package/dist/roap/types.js.map +1 -0
  62. package/dist/types/config.d.ts +1 -0
  63. package/dist/types/constants.d.ts +10 -0
  64. package/dist/types/controls-options-manager/enums.d.ts +3 -1
  65. package/dist/types/controls-options-manager/types.d.ts +7 -1
  66. package/dist/types/locus-info/index.d.ts +1 -0
  67. package/dist/types/meeting/in-meeting-actions.d.ts +10 -0
  68. package/dist/types/meeting/index.d.ts +50 -3
  69. package/dist/types/meeting/muteState.d.ts +0 -1
  70. package/dist/types/meeting/request.d.ts +12 -1
  71. package/dist/types/meeting/request.type.d.ts +6 -0
  72. package/dist/types/meeting/util.d.ts +8 -1
  73. package/dist/types/meeting-info/meeting-info-v2.d.ts +80 -0
  74. package/dist/types/meetings/index.d.ts +29 -0
  75. package/dist/types/member/index.d.ts +1 -0
  76. package/dist/types/metrics/constants.d.ts +9 -0
  77. package/dist/types/reachability/clusterReachability.d.ts +15 -7
  78. package/dist/types/reachability/index.d.ts +10 -1
  79. package/dist/types/reachability/reachability.types.d.ts +5 -0
  80. package/dist/types/roap/index.d.ts +3 -2
  81. package/dist/types/roap/turnDiscovery.d.ts +5 -17
  82. package/dist/types/roap/types.d.ts +16 -0
  83. package/dist/webinar/index.js +1 -1
  84. package/package.json +22 -22
  85. package/src/config.ts +1 -0
  86. package/src/constants.ts +17 -0
  87. package/src/controls-options-manager/enums.ts +2 -0
  88. package/src/controls-options-manager/types.ts +11 -1
  89. package/src/controls-options-manager/util.ts +62 -0
  90. package/src/locus-info/controlsUtils.ts +44 -14
  91. package/src/locus-info/index.ts +23 -1
  92. package/src/media/index.ts +5 -21
  93. package/src/meeting/in-meeting-actions.ts +20 -0
  94. package/src/meeting/index.ts +351 -99
  95. package/src/meeting/locusMediaRequest.ts +33 -23
  96. package/src/meeting/muteState.ts +0 -2
  97. package/src/meeting/request.ts +36 -1
  98. package/src/meeting/request.type.ts +7 -0
  99. package/src/meeting/util.ts +27 -2
  100. package/src/meeting-info/meeting-info-v2.ts +247 -6
  101. package/src/meetings/index.ts +87 -1
  102. package/src/meetings/util.ts +18 -0
  103. package/src/member/index.ts +11 -0
  104. package/src/member/util.ts +3 -0
  105. package/src/metrics/constants.ts +9 -0
  106. package/src/reachability/clusterReachability.ts +73 -26
  107. package/src/reachability/index.ts +70 -1
  108. package/src/reachability/reachability.types.ts +6 -0
  109. package/src/reachability/request.ts +7 -0
  110. package/src/reconnection-manager/index.ts +2 -2
  111. package/src/recording-controller/util.ts +17 -13
  112. package/src/roap/index.ts +3 -7
  113. package/src/roap/turnDiscovery.ts +34 -39
  114. package/src/roap/types.ts +23 -0
  115. package/test/unit/spec/controls-options-manager/util.js +120 -0
  116. package/test/unit/spec/locus-info/controlsUtils.js +103 -9
  117. package/test/unit/spec/locus-info/index.js +28 -0
  118. package/test/unit/spec/media/index.ts +6 -16
  119. package/test/unit/spec/meeting/in-meeting-actions.ts +13 -4
  120. package/test/unit/spec/meeting/index.js +558 -145
  121. package/test/unit/spec/meeting/locusMediaRequest.ts +101 -88
  122. package/test/unit/spec/meeting/muteState.js +0 -2
  123. package/test/unit/spec/meeting/request.js +32 -1
  124. package/test/unit/spec/meeting/utils.js +123 -18
  125. package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
  126. package/test/unit/spec/meetings/index.js +96 -1
  127. package/test/unit/spec/member/index.js +7 -0
  128. package/test/unit/spec/member/util.js +24 -0
  129. package/test/unit/spec/reachability/clusterReachability.ts +88 -56
  130. package/test/unit/spec/reachability/index.ts +101 -0
  131. package/test/unit/spec/reachability/request.js +47 -2
  132. package/test/unit/spec/reconnection-manager/index.js +4 -4
  133. package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -1,14 +1,18 @@
1
1
  import 'jsdom-global/register';
2
2
  import sinon from 'sinon';
3
3
  import {assert} from '@webex/test-helper-chai';
4
- import { cloneDeep } from 'lodash';
5
-
4
+ import {cloneDeep} from 'lodash';
5
+ import {EventEmitter} from 'events';
6
6
  import MockWebex from '@webex/test-helper-mock-webex';
7
7
  import Meetings from '@webex/plugin-meetings';
8
- import { LocalMuteRequest, LocusMediaRequest, RoapRequest } from "@webex/plugin-meetings/src/meeting/locusMediaRequest";
8
+ import {
9
+ LocalMuteRequest,
10
+ LocusMediaRequest,
11
+ RoapRequest,
12
+ } from '@webex/plugin-meetings/src/meeting/locusMediaRequest';
9
13
  import testUtils from '../../../utils/testUtils';
10
- import { Defer } from '@webex/common';
11
- import { IP_VERSION } from '../../../../src/constants';
14
+ import {Defer} from '@webex/common';
15
+ import {IP_VERSION} from '../../../../src/constants';
12
16
 
13
17
  describe('LocusMediaRequest.send()', () => {
14
18
  let locusMediaRequest: LocusMediaRequest;
@@ -16,10 +20,10 @@ describe('LocusMediaRequest.send()', () => {
16
20
  let mockWebex;
17
21
 
18
22
  const fakeLocusResponse = {
19
- locus: { something: 'whatever'}
23
+ locus: {something: 'whatever'},
20
24
  };
21
25
 
22
- const exampleRoapRequestBody:RoapRequest = {
26
+ const exampleRoapRequestBody: RoapRequest = {
23
27
  type: 'RoapMessage',
24
28
  mediaId: 'mediaId',
25
29
  selfUrl: 'fakeMeetingSelfUrl',
@@ -31,8 +35,11 @@ describe('LocusMediaRequest.send()', () => {
31
35
  tieBreaker: 0xfffffffe,
32
36
  },
33
37
  reachability: {
34
- 'wjfkm.wjfkm.*': {udp:{reachable: true}, tcp:{reachable:false}},
35
- '1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*': {udp:{reachable: false}, tcp:{reachable:true}},
38
+ 'wjfkm.wjfkm.*': {udp: {reachable: true}, tcp: {reachable: false}},
39
+ '1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*': {
40
+ udp: {reachable: false},
41
+ tcp: {reachable: true},
42
+ },
36
43
  },
37
44
  clientMediaPreferences: {
38
45
  preferTranscoding: false,
@@ -45,19 +52,22 @@ describe('LocusMediaRequest.send()', () => {
45
52
  reachability: {
46
53
  version: '1',
47
54
  result: 'some fake reachability result',
48
- }
49
- }
55
+ },
56
+ },
50
57
  };
51
58
 
52
- const createExpectedRoapBody = (expectedMessageType, expectedMute:{audioMuted: boolean, videoMuted: boolean}) => {
59
+ const createExpectedRoapBody = (
60
+ expectedMessageType,
61
+ expectedMute: {audioMuted: boolean; videoMuted: boolean}
62
+ ) => {
53
63
  return {
54
- device: { url: 'deviceUrl', deviceType: 'deviceType', regionCode: 'regionCode' },
64
+ device: {url: 'deviceUrl', deviceType: 'deviceType', regionCode: 'regionCode'},
55
65
  correlationId: 'correlationId',
56
66
  localMedias: [
57
67
  {
58
68
  localSdp: `{"audioMuted":${expectedMute.audioMuted},"videoMuted":${expectedMute.videoMuted},"roapMessage":{"messageType":"${expectedMessageType}","sdps":["sdp"],"version":"2","seq":1,"tieBreaker":4294967294},"reachability":{"wjfkm.wjfkm.*":{"udp":{"reachable":true},"tcp":{"reachable":false}},"1eb65fdf-9643-417f-9974-ad72cae0e10f.59268c12-7a04-4b23-a1a1-4c74be03019a.*":{"udp":{"reachable":false},"tcp":{"reachable":true}}}}`,
59
- mediaId: 'mediaId'
60
- }
69
+ mediaId: 'mediaId',
70
+ },
61
71
  ],
62
72
  clientMediaPreferences: {
63
73
  preferTranscoding: false,
@@ -65,24 +75,27 @@ describe('LocusMediaRequest.send()', () => {
65
75
  joinCookie: {
66
76
  anycastEntryPoint: 'aws-eu-west-1',
67
77
  clientIpAddress: 'some ip',
68
- timeShot: '2023-05-23T08:03:49Z'
78
+ timeShot: '2023-05-23T08:03:49Z',
69
79
  },
70
80
  reachability: {
71
81
  version: '1',
72
82
  result: 'some fake reachability result',
73
- }
74
- }
83
+ },
84
+ },
75
85
  };
76
86
  };
77
87
 
78
- const exampleLocalMuteRequestBody:LocalMuteRequest = {
88
+ const exampleLocalMuteRequestBody: LocalMuteRequest = {
79
89
  type: 'LocalMute',
80
90
  mediaId: 'mediaId',
81
91
  selfUrl: 'fakeMeetingSelfUrl',
82
92
  muteOptions: {},
83
93
  };
84
94
 
85
- const createExpectedLocalMuteBody = (expectedMute:{audioMuted: boolean, videoMuted: boolean}, sequence = undefined) => {
95
+ const createExpectedLocalMuteBody = (
96
+ expectedMute: {audioMuted: boolean; videoMuted: boolean},
97
+ sequence = undefined
98
+ ) => {
86
99
  const body: any = {
87
100
  device: {
88
101
  url: 'deviceUrl',
@@ -116,33 +129,37 @@ describe('LocusMediaRequest.send()', () => {
116
129
 
117
130
  mockWebex.internal = {
118
131
  newMetrics: {
119
- submitClientEvent: sinon.stub()
132
+ submitClientEvent: sinon.stub(),
120
133
  },
121
134
  };
122
135
 
123
- locusMediaRequest = new LocusMediaRequest({
124
- device: {
125
- url: 'deviceUrl',
126
- deviceType: 'deviceType',
127
- regionCode: 'regionCode',
136
+ locusMediaRequest = new LocusMediaRequest(
137
+ {
138
+ device: {
139
+ url: 'deviceUrl',
140
+ deviceType: 'deviceType',
141
+ regionCode: 'regionCode',
142
+ },
143
+ correlationId: 'correlationId',
144
+ meetingId: 'meetingId',
145
+ preferTranscoding: true,
128
146
  },
129
- correlationId: 'correlationId',
130
- meetingId: 'meetingId',
131
- preferTranscoding: true,
132
- }, {
133
- parent: mockWebex,
134
- });
147
+ {
148
+ parent: mockWebex,
149
+ }
150
+ );
135
151
  webexRequestStub = sinon.stub(locusMediaRequest, 'request').resolves(fakeLocusResponse);
136
- })
152
+ });
137
153
 
138
- const sendLocalMute = (muteOptions, overrides={}) => locusMediaRequest.send({...exampleLocalMuteRequestBody, ...overrides, muteOptions});
154
+ const sendLocalMute = (muteOptions, overrides = {}) =>
155
+ locusMediaRequest.send({...exampleLocalMuteRequestBody, ...overrides, muteOptions});
139
156
 
140
157
  const sendRoapMessage = (messageType) => {
141
158
  const request = cloneDeep(exampleRoapRequestBody);
142
159
 
143
160
  request.roapMessage.messageType = messageType;
144
161
  return locusMediaRequest.send(request);
145
- }
162
+ };
146
163
 
147
164
  /** Helper function that makes sure the LocusMediaRequest.confluenceState is 'created' */
148
165
  const ensureConfluenceCreated = async () => {
@@ -150,27 +167,7 @@ describe('LocusMediaRequest.send()', () => {
150
167
 
151
168
  webexRequestStub.resetHistory();
152
169
  mockWebex.internal.newMetrics.submitClientEvent.resetHistory();
153
- }
154
-
155
- const checkMetrics = (expectedMetrics: boolean = true) => {
156
- if (expectedMetrics) {
157
- assert.calledWith(mockWebex.internal.newMetrics.submitClientEvent, {
158
- name: 'client.locus.media.request',
159
- options: {
160
- meetingId: 'meetingId',
161
- },
162
- });
163
-
164
- assert.calledWith(mockWebex.internal.newMetrics.submitClientEvent, {
165
- name: 'client.locus.media.response',
166
- options: {
167
- meetingId: 'meetingId',
168
- },
169
- });
170
- } else {
171
- assert.notCalled(mockWebex.internal.newMetrics.submitClientEvent);
172
- }
173
- }
170
+ };
174
171
 
175
172
  it('sends a roap message', async () => {
176
173
  const result = await sendRoapMessage('OFFER');
@@ -181,14 +178,19 @@ describe('LocusMediaRequest.send()', () => {
181
178
  method: 'PUT',
182
179
  uri: 'fakeMeetingSelfUrl/media',
183
180
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
181
+ upload: sinon.match.instanceOf(EventEmitter),
182
+ download: sinon.match.instanceOf(EventEmitter),
184
183
  });
185
-
186
- checkMetrics();
187
184
  });
188
185
 
189
186
  it('sends correct metric event when roap message fails', async () => {
190
187
  webexRequestStub.rejects({code: 300, message: 'fake error'});
191
- await assert.isRejected(sendRoapMessage('OFFER'));
188
+
189
+ const promise = sendRoapMessage('OFFER');
190
+ await assert.isRejected(promise);
191
+
192
+ const error = await promise.catch((err) => err);
193
+ assert.isTrue(error.handledBySdk);
192
194
 
193
195
  assert.calledWith(mockWebex.internal.newMetrics.submitClientEvent, {
194
196
  name: 'client.locus.media.response',
@@ -210,9 +212,9 @@ describe('LocusMediaRequest.send()', () => {
210
212
  method: 'PUT',
211
213
  uri: 'fakeMeetingSelfUrl/media',
212
214
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}),
215
+ upload: sinon.match.instanceOf(EventEmitter),
216
+ download: sinon.match.instanceOf(EventEmitter),
213
217
  });
214
-
215
- checkMetrics(false);
216
218
  });
217
219
 
218
220
  it('sends a local mute request with sequence', async () => {
@@ -228,6 +230,8 @@ describe('LocusMediaRequest.send()', () => {
228
230
  method: 'PUT',
229
231
  uri: 'fakeMeetingSelfUrl/media',
230
232
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: false}, sequence),
233
+ upload: sinon.match.instanceOf(EventEmitter),
234
+ download: sinon.match.instanceOf(EventEmitter),
231
235
  });
232
236
  });
233
237
 
@@ -237,15 +241,13 @@ describe('LocusMediaRequest.send()', () => {
237
241
  let result1;
238
242
  let result2;
239
243
 
240
- const promise1 = sendLocalMute({audioMuted: true, videoMuted: false})
241
- .then((result) => {
242
- result1 = result;
243
- });
244
+ const promise1 = sendLocalMute({audioMuted: true, videoMuted: false}).then((result) => {
245
+ result1 = result;
246
+ });
244
247
 
245
- const promise2 = sendLocalMute({audioMuted: false, videoMuted: true})
246
- .then((result) => {
247
- result2 = result;
248
- });
248
+ const promise2 = sendLocalMute({audioMuted: false, videoMuted: true}).then((result) => {
249
+ result2 = result;
250
+ });
249
251
 
250
252
  await testUtils.flushPromises();
251
253
 
@@ -258,9 +260,9 @@ describe('LocusMediaRequest.send()', () => {
258
260
  method: 'PUT',
259
261
  uri: 'fakeMeetingSelfUrl/media',
260
262
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
263
+ upload: sinon.match.instanceOf(EventEmitter),
264
+ download: sinon.match.instanceOf(EventEmitter),
261
265
  });
262
-
263
- checkMetrics(false);
264
266
  });
265
267
 
266
268
  it('sends a local mute request with the last audio/video mute values', async () => {
@@ -277,9 +279,9 @@ describe('LocusMediaRequest.send()', () => {
277
279
  method: 'PUT',
278
280
  uri: 'fakeMeetingSelfUrl/media',
279
281
  body: createExpectedLocalMuteBody({audioMuted: true, videoMuted: false}),
282
+ upload: sinon.match.instanceOf(EventEmitter),
283
+ download: sinon.match.instanceOf(EventEmitter),
280
284
  });
281
-
282
- checkMetrics(false);
283
285
  });
284
286
 
285
287
  it('sends only roap when roap and local mute are requested', async () => {
@@ -296,9 +298,9 @@ describe('LocusMediaRequest.send()', () => {
296
298
  method: 'PUT',
297
299
  uri: 'fakeMeetingSelfUrl/media',
298
300
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: false}),
301
+ upload: sinon.match.instanceOf(EventEmitter),
302
+ download: sinon.match.instanceOf(EventEmitter),
299
303
  });
300
-
301
- checkMetrics();
302
304
  });
303
305
 
304
306
  describe('queueing', () => {
@@ -328,10 +330,10 @@ describe('LocusMediaRequest.send()', () => {
328
330
  * after the processing cycle from which it was called is finished.
329
331
  * This helper function waits for this to happen - it's needed, because we're using
330
332
  * fake timers in these tests
331
- */
333
+ */
332
334
  const ensureQueueProcessingIsStarted = () => {
333
335
  clock.tick(1);
334
- }
336
+ };
335
337
  it('queues requests if there is one already in progress', async () => {
336
338
  results.push(sendRoapMessage('OFFER'));
337
339
 
@@ -342,6 +344,8 @@ describe('LocusMediaRequest.send()', () => {
342
344
  method: 'PUT',
343
345
  uri: 'fakeMeetingSelfUrl/media',
344
346
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
347
+ upload: sinon.match.instanceOf(EventEmitter),
348
+ download: sinon.match.instanceOf(EventEmitter),
345
349
  });
346
350
 
347
351
  webexRequestStub.resetHistory();
@@ -364,6 +368,8 @@ describe('LocusMediaRequest.send()', () => {
364
368
  method: 'PUT',
365
369
  uri: 'fakeMeetingSelfUrl/media',
366
370
  body: createExpectedRoapBody('OK', {audioMuted: true, videoMuted: true}),
371
+ upload: sinon.match.instanceOf(EventEmitter),
372
+ download: sinon.match.instanceOf(EventEmitter),
367
373
  });
368
374
 
369
375
  // promise returned by the first call to send OFFER should be resolved by now
@@ -386,6 +392,8 @@ describe('LocusMediaRequest.send()', () => {
386
392
  method: 'PUT',
387
393
  uri: 'fakeMeetingSelfUrl/media',
388
394
  body: createExpectedRoapBody('OFFER', {audioMuted: false, videoMuted: false}),
395
+ upload: sinon.match.instanceOf(EventEmitter),
396
+ download: sinon.match.instanceOf(EventEmitter),
389
397
  });
390
398
 
391
399
  webexRequestStub.resetHistory();
@@ -410,6 +418,8 @@ describe('LocusMediaRequest.send()', () => {
410
418
  method: 'PUT',
411
419
  uri: 'fakeMeetingSelfUrl/media',
412
420
  body: createExpectedRoapBody('OK', {audioMuted: false, videoMuted: true}),
421
+ upload: sinon.match.instanceOf(EventEmitter),
422
+ download: sinon.match.instanceOf(EventEmitter),
413
423
  });
414
424
 
415
425
  // promise returned by the first call to send OFFER should be resolved by now
@@ -438,16 +448,17 @@ describe('LocusMediaRequest.send()', () => {
438
448
 
439
449
  ensureQueueProcessingIsStarted();
440
450
 
441
- sendLocalMute({audioMuted: false, videoMuted: true})
442
- .then((response) => {
443
- result = response;
444
- });
451
+ sendLocalMute({audioMuted: false, videoMuted: true}).then((response) => {
452
+ result = response;
453
+ });
445
454
 
446
455
  // only roap offer should have been sent so far
447
456
  assert.calledOnceWithExactly(webexRequestStub, {
448
457
  method: 'PUT',
449
458
  uri: 'fakeMeetingSelfUrl/media',
450
459
  body: createExpectedRoapBody('OFFER', {audioMuted: true, videoMuted: true}),
460
+ upload: sinon.match.instanceOf(EventEmitter),
461
+ download: sinon.match.instanceOf(EventEmitter),
451
462
  });
452
463
  assert.equal(result, undefined); // sendLocalMute shouldn't resolve yet, as the request should be queued
453
464
  assert.equal(locusMediaRequest.isConfluenceCreated(), false);
@@ -464,10 +475,12 @@ describe('LocusMediaRequest.send()', () => {
464
475
  method: 'PUT',
465
476
  uri: 'fakeMeetingSelfUrl/media',
466
477
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
478
+ upload: sinon.match.instanceOf(EventEmitter),
479
+ download: sinon.match.instanceOf(EventEmitter),
467
480
  });
468
481
 
469
482
  // check also the result once Locus replies to local mute
470
- const fakeLocusResponse = { response: 'ok'};
483
+ const fakeLocusResponse = {response: 'ok'};
471
484
  requestsToLocus[1].resolve(fakeLocusResponse);
472
485
  await testUtils.flushPromises();
473
486
  assert.deepEqual(result, fakeLocusResponse);
@@ -487,10 +500,9 @@ describe('LocusMediaRequest.send()', () => {
487
500
  assert.equal(locusMediaRequest.isConfluenceCreated(), true);
488
501
 
489
502
  // now send local mute
490
- sendLocalMute({audioMuted: false, videoMuted: true})
491
- .then((response) => {
492
- result = response;
493
- });
503
+ sendLocalMute({audioMuted: false, videoMuted: true}).then((response) => {
504
+ result = response;
505
+ });
494
506
 
495
507
  ensureQueueProcessingIsStarted();
496
508
 
@@ -499,8 +511,9 @@ describe('LocusMediaRequest.send()', () => {
499
511
  method: 'PUT',
500
512
  uri: 'fakeMeetingSelfUrl/media',
501
513
  body: createExpectedLocalMuteBody({audioMuted: false, videoMuted: true}),
514
+ upload: sinon.match.instanceOf(EventEmitter),
515
+ download: sinon.match.instanceOf(EventEmitter),
502
516
  });
503
517
  });
504
-
505
518
  });
506
- })
519
+ });
@@ -2,7 +2,6 @@ import sinon from 'sinon';
2
2
  import {assert} from '@webex/test-helper-chai';
3
3
  import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
4
4
  import {createMuteState, MuteState} from '@webex/plugin-meetings/src/meeting/muteState';
5
- import PermissionError from '@webex/plugin-meetings/src/common/errors/permission';
6
5
  import {AUDIO, VIDEO} from '@webex/plugin-meetings/src/constants';
7
6
 
8
7
  import testUtils from '../../../utils/testUtils';
@@ -39,7 +38,6 @@ describe('plugin-meetings', () => {
39
38
  unmuteAllowed: true,
40
39
  remoteVideoMuted: false,
41
40
  unmuteVideoAllowed: true,
42
-
43
41
  locusInfo: {
44
42
  handleLocusDelta: sinon.stub(),
45
43
  },
@@ -1,4 +1,5 @@
1
1
  import 'jsdom-global/register';
2
+ import {v4 as uuidv4} from 'uuid';
2
3
  import sinon from 'sinon';
3
4
  import {assert} from '@webex/test-helper-chai';
4
5
  import MockWebex from '@webex/test-helper-mock-webex';
@@ -205,7 +206,7 @@ describe('plugin-meetings', () => {
205
206
  roapMessage,
206
207
  reachability,
207
208
  permissionToken,
208
- clientMediaPreferences
209
+ clientMediaPreferences,
209
210
  });
210
211
  const requestParams = meetingsRequest.request.getCall(0).args[0];
211
212
 
@@ -630,6 +631,36 @@ describe('plugin-meetings', () => {
630
631
  });
631
632
  });
632
633
 
634
+ describe('#setPostMeetingDataConsent', () => {
635
+ [true, false].forEach((consent) => {
636
+ it(`sends request to set post meeting data consent with ${consent}`, async () => {
637
+ const locusUrl = `https://locus-test.wbx2.com/locus/api/v1/loci/${consent}`;
638
+ const selfId = uuidv4();
639
+ const deviceUrl = `https://wdm-test.wbx2.com/wdm/api/v1/devices/${consent}`;
640
+
641
+ const consentPromise = meetingsRequest.setPostMeetingDataConsent({
642
+ postMeetingDataConsent: consent,
643
+ locusUrl,
644
+ selfId,
645
+ deviceUrl,
646
+ });
647
+ assert.exists(consentPromise.then);
648
+ await consentPromise;
649
+
650
+ checkRequest({
651
+ method: 'PATCH',
652
+ uri: `${locusUrl}/participant/${selfId}/controls`,
653
+ body: {
654
+ consent: {
655
+ postMeetingDataConsent: consent,
656
+ deviceUrl,
657
+ },
658
+ },
659
+ });
660
+ });
661
+ });
662
+ });
663
+
633
664
  describe('#prepareLeaveMeetingRequestOptions', () => {
634
665
  it('returns expected result', async () => {
635
666
  const result = meetingsRequest.prepareLeaveMeetingRequestOptions({
@@ -66,7 +66,7 @@ describe('plugin-meetings', () => {
66
66
 
67
67
  describe('#cleanup', () => {
68
68
  it('do clean up on meeting object with LLM enabled', async () => {
69
- meeting.config = {enableAutomaticLLM : true};
69
+ meeting.config = {enableAutomaticLLM: true};
70
70
  await MeetingUtil.cleanUp(meeting);
71
71
  assert.calledOnce(meeting.cleanupLocalStreams);
72
72
  assert.calledOnce(meeting.closeRemoteStreams);
@@ -84,7 +84,7 @@ describe('plugin-meetings', () => {
84
84
  });
85
85
 
86
86
  it('do clean up on meeting object with LLM disabled', async () => {
87
- meeting.config = {enableAutomaticLLM : false};
87
+ meeting.config = {enableAutomaticLLM: false};
88
88
  await MeetingUtil.cleanUp(meeting);
89
89
  assert.calledOnce(meeting.cleanupLocalStreams);
90
90
  assert.calledOnce(meeting.closeRemoteStreams);
@@ -392,8 +392,7 @@ describe('plugin-meetings', () => {
392
392
  meetingJoinUrl: 'meetingJoinUrl',
393
393
  locusUrl: 'locusUrl',
394
394
  meetingRequest: {
395
- joinMeeting: sinon.stub().returns(
396
- Promise.resolve(joinMeetingResponse)),
395
+ joinMeeting: sinon.stub().returns(Promise.resolve(joinMeetingResponse)),
397
396
  },
398
397
  getWebexObject: sinon.stub().returns(webex),
399
398
  setLocus: sinon.stub(),
@@ -410,23 +409,29 @@ describe('plugin-meetings', () => {
410
409
  id: 'fake client media preferences',
411
410
  };
412
411
 
413
- webex.meetings.reachability.getReachabilityReportToAttachToRoap.resolves(FAKE_REACHABILITY_REPORT);
414
- webex.meetings.reachability.getClientMediaPreferences.resolves(FAKE_CLIENT_MEDIA_PREFERENCES);
412
+ webex.meetings.reachability.getReachabilityReportToAttachToRoap.resolves(
413
+ FAKE_REACHABILITY_REPORT
414
+ );
415
+ webex.meetings.reachability.getClientMediaPreferences.resolves(
416
+ FAKE_CLIENT_MEDIA_PREFERENCES
417
+ );
415
418
 
416
- sinon
417
- .stub(webex.internal.device.ipNetworkDetector, 'supportsIpV4')
418
- .get(() => true);
419
- sinon
420
- .stub(webex.internal.device.ipNetworkDetector, 'supportsIpV6')
421
- .get(() => true);
419
+ sinon.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV4').get(() => true);
420
+ sinon.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV6').get(() => true);
422
421
 
423
422
  await MeetingUtil.joinMeeting(meeting, {
424
423
  reachability: 'reachability',
425
424
  roapMessage: 'roapMessage',
426
425
  });
427
426
 
428
- assert.calledOnceWithExactly(webex.meetings.reachability.getReachabilityReportToAttachToRoap);
429
- assert.calledOnceWithExactly(webex.meetings.reachability.getClientMediaPreferences, meeting.isMultistream, IP_VERSION.ipv4_and_ipv6);
427
+ assert.calledOnceWithExactly(
428
+ webex.meetings.reachability.getReachabilityReportToAttachToRoap
429
+ );
430
+ assert.calledOnceWithExactly(
431
+ webex.meetings.reachability.getClientMediaPreferences,
432
+ meeting.isMultistream,
433
+ IP_VERSION.ipv4_and_ipv6
434
+ );
430
435
 
431
436
  assert.calledOnce(meeting.meetingRequest.joinMeeting);
432
437
  const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
@@ -436,9 +441,9 @@ describe('plugin-meetings', () => {
436
441
  assert.equal(parameter.clientMediaPreferences, FAKE_CLIENT_MEDIA_PREFERENCES);
437
442
  assert.equal(parameter.roapMessage, 'roapMessage');
438
443
 
439
- assert.calledOnce(meeting.setLocus)
444
+ assert.calledOnce(meeting.setLocus);
440
445
  const setLocusParameter = meeting.setLocus.getCall(0).args[0];
441
- assert.deepEqual(setLocusParameter, MeetingUtil.parseLocusJoin(joinMeetingResponse))
446
+ assert.deepEqual(setLocusParameter, MeetingUtil.parseLocusJoin(joinMeetingResponse));
442
447
 
443
448
  assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
444
449
  name: 'client.locus.join.request',
@@ -460,6 +465,49 @@ describe('plugin-meetings', () => {
460
465
  });
461
466
  });
462
467
 
468
+ it('#Should call `meetingRequest.joinMeeting and handle a date header in the response : isoLocalClientMeetingJoinedTime', async () => {
469
+ meeting.isMultistream = true;
470
+
471
+ const FAKE_REACHABILITY_REPORT = {
472
+ id: 'fake reachability report',
473
+ };
474
+ const FAKE_CLIENT_MEDIA_PREFERENCES = {
475
+ id: 'fake client media preferences',
476
+ };
477
+
478
+ webex.meetings.reachability.getReachabilityReportToAttachToRoap.resolves(
479
+ FAKE_REACHABILITY_REPORT
480
+ );
481
+ webex.meetings.reachability.getClientMediaPreferences.resolves(
482
+ FAKE_CLIENT_MEDIA_PREFERENCES
483
+ );
484
+
485
+ sinon.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV4').get(() => true);
486
+ sinon.stub(webex.internal.device.ipNetworkDetector, 'supportsIpV6').get(() => true);
487
+
488
+ meeting.meetingRequest.joinMeeting.resolves({
489
+ headers: {
490
+ date: 'test',
491
+ },
492
+ body: {
493
+ mediaConnections: [{mediaId: 'test'}],
494
+ locus: {
495
+ url: 'test',
496
+ self: {
497
+ id: 'test',
498
+ },
499
+ },
500
+ },
501
+ });
502
+
503
+ await MeetingUtil.joinMeeting(meeting, {
504
+ reachability: 'reachability',
505
+ roapMessage: 'roapMessage',
506
+ });
507
+
508
+ assert.equal(meeting.isoLocalClientMeetingJoinTime, 'test');
509
+ });
510
+
463
511
  it('should handle failed reachability report retrieval', async () => {
464
512
  webex.meetings.reachability.getReachabilityReportToAttachToRoap.rejects(
465
513
  new Error('fake error')
@@ -516,6 +564,17 @@ describe('plugin-meetings', () => {
516
564
  assert.equal(parameter.liveAnnotationSupported, true);
517
565
  });
518
566
 
567
+ it('#Should call meetingRequest.joinMeeting with alias passed through', async () => {
568
+ await MeetingUtil.joinMeeting(meeting, {
569
+ alias: 'alias name',
570
+ });
571
+
572
+ assert.calledOnce(meeting.meetingRequest.joinMeeting);
573
+ const parameter = meeting.meetingRequest.joinMeeting.getCall(0).args[0];
574
+
575
+ assert.equal(parameter.alias, 'alias name');
576
+ });
577
+
519
578
  it('#Should call meetingRequest.joinMeeting with locale=en_UK, deviceCapabilities=["TEST"] when they are passed in as those values', async () => {
520
579
  await MeetingUtil.joinMeeting(meeting, {
521
580
  locale: 'en_UK',
@@ -685,6 +744,18 @@ describe('plugin-meetings', () => {
685
744
  });
686
745
  });
687
746
 
747
+ describe('requiresPostMeetingDataConsentPrompt', () => {
748
+ it('works as expected', () => {
749
+ assert.deepEqual(
750
+ MeetingUtil.requiresPostMeetingDataConsentPrompt([
751
+ 'SHOW_POST_MEETING_DATA_CONSENT_PROMPT',
752
+ ]),
753
+ true
754
+ );
755
+ assert.deepEqual(MeetingUtil.requiresPostMeetingDataConsentPrompt([]), false);
756
+ });
757
+ });
758
+
688
759
  describe('canUserRenameOthers', () => {
689
760
  it('works as expected', () => {
690
761
  assert.deepEqual(MeetingUtil.canUserRenameOthers(['CAN_RENAME_OTHERS']), true);
@@ -694,8 +765,34 @@ describe('plugin-meetings', () => {
694
765
 
695
766
  describe('canShareWhiteBoard', () => {
696
767
  it('works as expected', () => {
697
- assert.deepEqual(MeetingUtil.canShareWhiteBoard(['SHARE_WHITEBOARD']), true);
698
- assert.deepEqual(MeetingUtil.canShareWhiteBoard([]), false);
768
+ assert.deepEqual(
769
+ MeetingUtil.canShareWhiteBoard(['SHARE_WHITEBOARD'], {
770
+ [SELF_POLICY.SUPPORT_WHITEBOARD]: true,
771
+ }),
772
+ true
773
+ );
774
+ assert.deepEqual(
775
+ MeetingUtil.canShareWhiteBoard([], {
776
+ [SELF_POLICY.SUPPORT_WHITEBOARD]: true,
777
+ }),
778
+ false
779
+ );
780
+ assert.deepEqual(
781
+ MeetingUtil.canShareWhiteBoard(['SHARE_WHITEBOARD'], {
782
+ [SELF_POLICY.SUPPORT_WHITEBOARD]: false,
783
+ }),
784
+ false
785
+ );
786
+ assert.deepEqual(
787
+ MeetingUtil.canShareWhiteBoard([], {
788
+ [SELF_POLICY.SUPPORT_WHITEBOARD]: false,
789
+ }),
790
+ false
791
+ );
792
+ assert.deepEqual(
793
+ MeetingUtil.canShareWhiteBoard(['SHARE_WHITEBOARD'], undefined),
794
+ false
795
+ );
699
796
  });
700
797
  });
701
798
 
@@ -1088,6 +1185,14 @@ describe('plugin-meetings', () => {
1088
1185
  });
1089
1186
  });
1090
1187
 
1188
+ describe('markErrorAsHandledBySdk', () => {
1189
+ it('should set the error as handled', () => {
1190
+ const error = MeetingUtil.markErrorAsHandledBySdk(new Error('Test error'));
1191
+
1192
+ assert.isTrue(error.handledBySdk);
1193
+ })
1194
+ });
1195
+
1091
1196
  describe('getChangeMeetingFloorErrorPayload', () => {
1092
1197
  [
1093
1198
  {