@webex/plugin-meetings 3.4.0 → 3.5.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 (60) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/interpretation/index.js +1 -1
  4. package/dist/interpretation/siLanguage.js +1 -1
  5. package/dist/media/index.js +6 -9
  6. package/dist/media/index.js.map +1 -1
  7. package/dist/meeting/index.js +122 -49
  8. package/dist/meeting/index.js.map +1 -1
  9. package/dist/meeting/util.js +1 -0
  10. package/dist/meeting/util.js.map +1 -1
  11. package/dist/meetings/index.js +4 -2
  12. package/dist/meetings/index.js.map +1 -1
  13. package/dist/reachability/index.js +175 -103
  14. package/dist/reachability/index.js.map +1 -1
  15. package/dist/reconnection-manager/index.js +1 -1
  16. package/dist/reconnection-manager/index.js.map +1 -1
  17. package/dist/rtcMetrics/index.js +26 -6
  18. package/dist/rtcMetrics/index.js.map +1 -1
  19. package/dist/types/meeting/index.d.ts +11 -2
  20. package/dist/types/meetings/index.d.ts +2 -1
  21. package/dist/types/reachability/index.d.ts +14 -2
  22. package/dist/types/rtcMetrics/index.d.ts +11 -1
  23. package/dist/webinar/index.js +1 -1
  24. package/package.json +22 -22
  25. package/src/media/index.ts +5 -9
  26. package/src/meeting/index.ts +54 -10
  27. package/src/meeting/util.ts +2 -0
  28. package/src/meetings/index.ts +4 -3
  29. package/src/reachability/index.ts +49 -4
  30. package/src/reconnection-manager/index.ts +1 -1
  31. package/src/rtcMetrics/index.ts +25 -5
  32. package/test/integration/spec/converged-space-meetings.js +1 -1
  33. package/test/unit/spec/breakouts/index.ts +1 -0
  34. package/test/unit/spec/interceptors/locusRetry.ts +11 -10
  35. package/test/unit/spec/media/MediaConnectionAwaiter.ts +1 -0
  36. package/test/unit/spec/media/index.ts +34 -7
  37. package/test/unit/spec/media/properties.ts +1 -1
  38. package/test/unit/spec/meeting/connectionStateHandler.ts +1 -0
  39. package/test/unit/spec/meeting/index.js +77 -6
  40. package/test/unit/spec/meeting/locusMediaRequest.ts +3 -2
  41. package/test/unit/spec/meeting/request.js +1 -0
  42. package/test/unit/spec/meeting/utils.js +4 -0
  43. package/test/unit/spec/meeting-info/meetinginfov2.js +10 -11
  44. package/test/unit/spec/meeting-info/request.js +1 -1
  45. package/test/unit/spec/meetings/index.js +30 -3
  46. package/test/unit/spec/members/request.js +2 -1
  47. package/test/unit/spec/multistream/mediaRequestManager.ts +1 -0
  48. package/test/unit/spec/multistream/receiveSlot.ts +1 -0
  49. package/test/unit/spec/multistream/receiveSlotManager.ts +1 -0
  50. package/test/unit/spec/multistream/remoteMedia.ts +1 -0
  51. package/test/unit/spec/multistream/remoteMediaGroup.ts +1 -0
  52. package/test/unit/spec/multistream/remoteMediaManager.ts +1 -0
  53. package/test/unit/spec/multistream/sendSlotManager.ts +1 -0
  54. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +0 -1
  55. package/test/unit/spec/reachability/index.ts +211 -13
  56. package/test/unit/spec/reachability/request.js +1 -0
  57. package/test/unit/spec/roap/request.ts +1 -0
  58. package/test/unit/spec/rtcMetrics/index.ts +31 -0
  59. package/src/networkQualityMonitor/index.ts +0 -211
  60. package/test/unit/spec/networkQualityMonitor/index.js +0 -99
@@ -34,6 +34,8 @@ export default class RtcMetrics {
34
34
 
35
35
  connectionId: string;
36
36
 
37
+ shouldSendMetricsOnNextStatsReport: boolean;
38
+
37
39
  /**
38
40
  * Initialize the interval.
39
41
  *
@@ -47,9 +49,7 @@ export default class RtcMetrics {
47
49
  this.meetingId = meetingId;
48
50
  this.webex = webex;
49
51
  this.correlationId = correlationId;
50
- this.setNewConnectionId();
51
- // Send the first set of metrics at 5 seconds in the case of a user leaving the call shortly after joining.
52
- setTimeout(this.sendMetricsInQueue.bind(this), 5 * 1000);
52
+ this.resetConnection();
53
53
  }
54
54
 
55
55
  /**
@@ -64,6 +64,18 @@ export default class RtcMetrics {
64
64
  }
65
65
  }
66
66
 
67
+ /**
68
+ * Forces sending metrics when we get the next stats-report
69
+ *
70
+ * This is useful for cases when something important happens that affects the media connection,
71
+ * for example when we move from lobby into the meeting.
72
+ *
73
+ * @returns {void}
74
+ */
75
+ public sendNextMetrics() {
76
+ this.shouldSendMetricsOnNextStatsReport = true;
77
+ }
78
+
67
79
  /**
68
80
  * Add metrics items to the metrics queue.
69
81
  *
@@ -79,6 +91,13 @@ export default class RtcMetrics {
79
91
 
80
92
  this.metricsQueue.push(data);
81
93
 
94
+ if (this.shouldSendMetricsOnNextStatsReport && data.name === 'stats-report') {
95
+ // this is the first useful set of data (WCME gives it to us after 5s), send it out immediately
96
+ // in case the user is unhappy and closes the browser early
97
+ this.sendMetricsInQueue();
98
+ this.shouldSendMetricsOnNextStatsReport = false;
99
+ }
100
+
82
101
  try {
83
102
  // If a connection fails, send the rest of the metrics in queue and get a new connection id.
84
103
  const parsedPayload = parseJsonPayload(data.payload);
@@ -88,7 +107,7 @@ export default class RtcMetrics {
88
107
  parsedPayload.value === 'failed'
89
108
  ) {
90
109
  this.sendMetricsInQueue();
91
- this.setNewConnectionId();
110
+ this.resetConnection();
92
111
  }
93
112
  } catch (e) {
94
113
  console.error(e);
@@ -130,8 +149,9 @@ export default class RtcMetrics {
130
149
  *
131
150
  * @returns {void}
132
151
  */
133
- private setNewConnectionId() {
152
+ private resetConnection() {
134
153
  this.connectionId = uuid.v4();
154
+ this.shouldSendMetricsOnNextStatsReport = true;
135
155
  }
136
156
 
137
157
  /**
@@ -1,5 +1,5 @@
1
- import { config } from 'dotenv';
2
1
  import 'jsdom-global/register';
2
+ import {config} from 'dotenv';
3
3
  import {assert} from '@webex/test-helper-chai';
4
4
  import {skipInNode} from '@webex/test-helper-mocha';
5
5
  import BrowserDetection from '@webex/plugin-meetings/dist/common/browser-detection';
@@ -1,3 +1,4 @@
1
+ import 'jsdom-global/register';
1
2
  import {assert, expect} from '@webex/test-helper-chai';
2
3
  import Breakouts from '@webex/plugin-meetings/src/breakouts';
3
4
  import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  /* eslint-disable camelcase */
6
+ import 'jsdom-global/register';
6
7
  import {assert} from '@webex/test-helper-chai';
7
8
  import { expect } from "@webex/test-helper-chai";
8
9
  import MockWebex from '@webex/test-helper-mock-webex';
@@ -13,7 +14,7 @@ import sinon from 'sinon';
13
14
 
14
15
  describe('plugin-meetings', () => {
15
16
  describe('Interceptors', () => {
16
- describe('LocusRetryStatusInterceptor', () => {
17
+ describe('LocusRetryStatusInterceptor', () => {
17
18
  let interceptor, webex;
18
19
  beforeEach(() => {
19
20
  webex = new MockWebex({
@@ -24,7 +25,7 @@ describe('plugin-meetings', () => {
24
25
  interceptor = Reflect.apply(LocusRetryStatusInterceptor.create, {
25
26
  sessionId: 'mock-webex_uuid',
26
27
  }, []);
27
- });
28
+ });
28
29
  describe('#onResponseError', () => {
29
30
  const options = {
30
31
  method: 'POST',
@@ -41,7 +42,7 @@ describe('plugin-meetings', () => {
41
42
  headers: {
42
43
  trackingid: 'test',
43
44
  'retry-after': 1000,
44
- },
45
+ },
45
46
  uri: `https://locus-test.webex.com/locus/api/v1/loci/call`,
46
47
  },
47
48
  body: {
@@ -54,7 +55,7 @@ describe('plugin-meetings', () => {
54
55
  headers: {
55
56
  trackingid: 'test',
56
57
  'retry-after': 1000,
57
- },
58
+ },
58
59
  uri: `https://locus-test.webex.com/locus/api/v1/loci/call`,
59
60
  },
60
61
  body: {
@@ -73,7 +74,7 @@ describe('plugin-meetings', () => {
73
74
 
74
75
  return interceptor.onResponseError(options, reason2).then(() => {
75
76
  expect(handleRetryStub.calledWith(options, 1000)).to.be.true;
76
-
77
+
77
78
  });
78
79
  });
79
80
  });
@@ -92,7 +93,7 @@ describe('plugin-meetings', () => {
92
93
  it('returns the correct resolved value when the request is successful', () => {
93
94
  const mockResponse = 'mock response'
94
95
  interceptor.webex.request = sinon.stub().returns(Promise.resolve(mockResponse));
95
-
96
+
96
97
  return interceptor.handleRetryRequestLocusServiceError(options, retryAfterTime)
97
98
  .then((response) => {
98
99
  expect(response).to.equal(mockResponse);
@@ -101,9 +102,9 @@ describe('plugin-meetings', () => {
101
102
 
102
103
  it('rejects the promise when the request is unsuccessful', () => {
103
104
  const rejectionReason = 'Service Unavaialble after retry';
104
-
105
+
105
106
  interceptor.webex.request = sinon.stub().returns(Promise.reject(rejectionReason));
106
-
107
+
107
108
  return interceptor.handleRetryRequestLocusServiceError(options, retryAfterTime)
108
109
  .catch((error) => {
109
110
  expect(error).to.equal(rejectionReason);
@@ -114,10 +115,10 @@ describe('plugin-meetings', () => {
114
115
  let clock;
115
116
  clock = sinon.useFakeTimers();
116
117
  const mockResponse = 'mock response'
117
-
118
+
118
119
  interceptor.webex.request = sinon.stub().returns(Promise.resolve(mockResponse));
119
120
  const promise = interceptor.handleRetryRequestLocusServiceError(options, retryAfterTime);
120
-
121
+
121
122
  clock.tick(retryAfterTime);
122
123
 
123
124
  return promise.then(() => {
@@ -1,3 +1,4 @@
1
+ import 'jsdom-global/register';
1
2
  import {assert} from '@webex/test-helper-chai';
2
3
  import sinon from 'sinon';
3
4
  import {ConnectionState, MediaConnectionEventNames} from '@webex/internal-media-core';
@@ -1,16 +1,15 @@
1
+ import 'jsdom-global/register';
1
2
  import * as InternalMediaCoreModule from '@webex/internal-media-core';
2
3
  import Media from '@webex/plugin-meetings/src/media/index';
3
4
  import {assert} from '@webex/test-helper-chai';
4
5
  import sinon from 'sinon';
5
6
  import StaticConfig from '@webex/plugin-meetings/src/common/config';
6
- import MockWebex from '@webex/test-helper-mock-webex';
7
7
 
8
8
  describe('createMediaConnection', () => {
9
9
  let clock;
10
10
  beforeEach(() => {
11
11
  clock = sinon.useFakeTimers();
12
12
  });
13
- const webex = MockWebex();
14
13
 
15
14
  const fakeRoapMediaConnection = {
16
15
  id: 'roap media connection',
@@ -61,7 +60,7 @@ describe('createMediaConnection', () => {
61
60
  const ENABLE_EXTMAP = false;
62
61
  const ENABLE_RTX = true;
63
62
 
64
- Media.createMediaConnection(false, 'some debug id', webex, 'meetingId', 'correlationId', {
63
+ Media.createMediaConnection(false, 'some debug id', 'meetingId', {
65
64
  mediaProperties: {
66
65
  mediaDirection: {
67
66
  sendAudio: false,
@@ -139,7 +138,13 @@ describe('createMediaConnection', () => {
139
138
  .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
140
139
  .returns(fakeRoapMediaConnection);
141
140
 
142
- Media.createMediaConnection(true, 'some debug id', webex, 'meeting id', 'correlationId', {
141
+ const rtcMetrics = {
142
+ addMetrics: sinon.stub(),
143
+ closeMetrics: sinon.stub(),
144
+ sendMetricsInQueue: sinon.stub(),
145
+ };
146
+
147
+ Media.createMediaConnection(true, 'some debug id', 'meeting id', {
143
148
  mediaProperties: {
144
149
  mediaDirection: {
145
150
  sendAudio: true,
@@ -150,6 +155,7 @@ describe('createMediaConnection', () => {
150
155
  receiveShare: true,
151
156
  },
152
157
  },
158
+ rtcMetrics,
153
159
  turnServerInfo: {
154
160
  url: 'turns:turn-server-url:443?transport=tcp',
155
161
  username: 'turn username',
@@ -177,6 +183,27 @@ describe('createMediaConnection', () => {
177
183
  },
178
184
  'meeting id'
179
185
  );
186
+
187
+ // check if rtcMetrics callbacks are configured correctly
188
+ const addMetricsCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[2];
189
+ const closeMetricsCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[3];
190
+ const sendMetricsInQueueCallback = multistreamRoapMediaConnectionConstructorStub.getCalls()[0].args[4];
191
+
192
+ assert.isFunction(addMetricsCallback);
193
+ assert.isFunction(closeMetricsCallback);
194
+ assert.isFunction(sendMetricsInQueueCallback);
195
+
196
+ const fakeMetricsData = {id: 'metrics data'};
197
+
198
+ addMetricsCallback(fakeMetricsData);
199
+ assert.calledOnceWithExactly(rtcMetrics.addMetrics, fakeMetricsData);
200
+
201
+ closeMetricsCallback();
202
+ assert.calledOnce(rtcMetrics.closeMetrics);
203
+
204
+ sendMetricsInQueueCallback();
205
+ assert.calledOnce(rtcMetrics.sendMetricsInQueue);
206
+
180
207
  });
181
208
 
182
209
  [
@@ -191,7 +218,7 @@ describe('createMediaConnection', () => {
191
218
  .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
192
219
  .returns(fakeRoapMediaConnection);
193
220
 
194
- Media.createMediaConnection(true, 'debug string', webex, 'meeting id', 'correlationId', {
221
+ Media.createMediaConnection(true, 'debug string', 'meeting id', {
195
222
  mediaProperties: {
196
223
  mediaDirection: {
197
224
  sendAudio: true,
@@ -220,7 +247,7 @@ describe('createMediaConnection', () => {
220
247
  .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
221
248
  .returns(fakeRoapMediaConnection);
222
249
 
223
- Media.createMediaConnection(true, 'debug string', webex, 'meeting id', 'correlationId', {
250
+ Media.createMediaConnection(true, 'debug string', 'meeting id', {
224
251
  mediaProperties: {
225
252
  mediaDirection: {
226
253
  sendAudio: true,
@@ -260,7 +287,7 @@ describe('createMediaConnection', () => {
260
287
  const ENABLE_EXTMAP = false;
261
288
  const ENABLE_RTX = true;
262
289
 
263
- Media.createMediaConnection(false, 'some debug id', webex, 'meeting id', 'correlationId', {
290
+ Media.createMediaConnection(false, 'some debug id', 'meeting id', {
264
291
  mediaProperties: {
265
292
  mediaDirection: {
266
293
  sendAudio: true,
@@ -1,8 +1,8 @@
1
+ import 'jsdom-global/register';
1
2
  import {assert} from '@webex/test-helper-chai';
2
3
  import sinon from 'sinon';
3
4
  import {ConnectionState} from '@webex/internal-media-core';
4
5
  import MediaProperties from '@webex/plugin-meetings/src/media/properties';
5
- import testUtils from '../../../utils/testUtils';
6
6
  import {Defer} from '@webex/common';
7
7
  import MediaConnectionAwaiter from '../../../../src/media/MediaConnectionAwaiter';
8
8
 
@@ -1,3 +1,4 @@
1
+ import 'jsdom-global/register'
1
2
  import sinon from 'sinon';
2
3
  import {assert} from '@webex/test-helper-chai';
3
4
  import {
@@ -5,6 +5,8 @@ import 'jsdom-global/register';
5
5
  import {cloneDeep, forEach, isEqual, isUndefined} from 'lodash';
6
6
  import sinon from 'sinon';
7
7
  import * as InternalMediaCoreModule from '@webex/internal-media-core';
8
+ import * as RtcMetricsModule from '@webex/plugin-meetings/src/rtcMetrics';
9
+ import * as RemoteMediaManagerModule from '@webex/plugin-meetings/src/multistream/remoteMediaManager';
8
10
  import StateMachine from 'javascript-state-machine';
9
11
  import uuid from 'uuid';
10
12
  import {assert, expect} from '@webex/test-helper-chai';
@@ -329,6 +331,7 @@ describe('plugin-meetings', () => {
329
331
  assert.isNull(meeting.partner);
330
332
  assert.isNull(meeting.type);
331
333
  assert.isNull(meeting.owner);
334
+ assert.isUndefined(meeting.isoLocalClientMeetingJoinTime);
332
335
  assert.isNull(meeting.hostId);
333
336
  assert.isNull(meeting.policy);
334
337
  assert.instanceOf(meeting.meetingRequest, MeetingRequest);
@@ -1585,6 +1588,10 @@ describe('plugin-meetings', () => {
1585
1588
  sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(joinMeetingResult));
1586
1589
  });
1587
1590
 
1591
+ afterEach(() => {
1592
+ assert.exists(meeting.isoLocalClientMeetingJoinTime);
1593
+ });
1594
+
1588
1595
  it('should join the meeting and return promise', async () => {
1589
1596
  const join = meeting.join({pstnAudioType: 'dial-in'});
1590
1597
  meeting.config.enableAutomaticLLM = true;
@@ -1603,6 +1610,7 @@ describe('plugin-meetings', () => {
1603
1610
  const result = await join;
1604
1611
 
1605
1612
  assert.calledOnce(MeetingUtil.joinMeeting);
1613
+ assert.calledOnce(webex.internal.device.meetingStarted);
1606
1614
  assert.calledOnce(meeting.setLocus);
1607
1615
  assert.equal(result, joinMeetingResult);
1608
1616
  assert.calledWith(webex.internal.llm.on, 'online', meeting.handleLLMOnline);
@@ -2405,9 +2413,7 @@ describe('plugin-meetings', () => {
2405
2413
  Media.createMediaConnection,
2406
2414
  false,
2407
2415
  meeting.getMediaConnectionDebugId(),
2408
- webex,
2409
2416
  meeting.id,
2410
- meeting.correlationId,
2411
2417
  sinon.match({turnServerInfo: undefined})
2412
2418
  );
2413
2419
  assert.calledOnce(meeting.setMercuryListener);
@@ -2449,6 +2455,44 @@ describe('plugin-meetings', () => {
2449
2455
  checkWorking({allowMediaInLobby: true});
2450
2456
  });
2451
2457
 
2458
+ it('should create rtcMetrics and pass them to Media.createMediaConnection()', async () => {
2459
+ const fakeRtcMetrics = {id: 'fake rtc metrics object'};
2460
+ const rtcMetricsCtor = sinon.stub(RtcMetricsModule, 'default').returns(fakeRtcMetrics);
2461
+
2462
+ // setup the minimum mocks required for multistream connection
2463
+ fakeMediaConnection.createSendSlot = sinon.stub().returns({
2464
+ publishStream: sinon.stub(),
2465
+ unpublishStream: sinon.stub(),
2466
+ setNamedMediaGroups: sinon.stub(),
2467
+ });
2468
+ sinon.stub(RemoteMediaManagerModule, 'RemoteMediaManager').returns({
2469
+ start: sinon.stub().resolves(),
2470
+ on: sinon.stub(),
2471
+ logAllReceiveSlots: sinon.stub(),
2472
+ });
2473
+
2474
+ meeting.meetingState = 'ACTIVE';
2475
+ meeting.isMultistream = true;
2476
+
2477
+ await meeting.addMedia({
2478
+ mediaSettings: {},
2479
+ });
2480
+
2481
+ assert.calledOnceWithExactly(rtcMetricsCtor, webex, meeting.id, meeting.correlationId);
2482
+
2483
+ // check that rtcMetrics was passed to Media.createMediaConnection
2484
+ assert.calledOnce(Media.createMediaConnection);
2485
+ assert.calledWith(
2486
+ Media.createMediaConnection,
2487
+ true,
2488
+ meeting.getMediaConnectionDebugId(),
2489
+ meeting.id,
2490
+ sinon.match({
2491
+ rtcMetrics: fakeRtcMetrics,
2492
+ })
2493
+ );
2494
+ });
2495
+
2452
2496
  it('should pass the turn server info to the peer connection', async () => {
2453
2497
  const FAKE_TURN_URL = 'turns:webex.com:3478';
2454
2498
  const FAKE_TURN_USER = 'some-turn-username';
@@ -2478,9 +2522,7 @@ describe('plugin-meetings', () => {
2478
2522
  Media.createMediaConnection,
2479
2523
  false,
2480
2524
  meeting.getMediaConnectionDebugId(),
2481
- webex,
2482
2525
  meeting.id,
2483
- meeting.correlationId,
2484
2526
  sinon.match({
2485
2527
  turnServerInfo: {
2486
2528
  url: FAKE_TURN_URL,
@@ -3023,6 +3065,8 @@ describe('plugin-meetings', () => {
3023
3065
  }),
3024
3066
  };
3025
3067
  meeting.iceCandidatesCount = 3;
3068
+ meeting.iceCandidateErrors.set('701_error', 3);
3069
+ meeting.iceCandidateErrors.set('701_turn_host_lookup_received_error', 1);
3026
3070
 
3027
3071
  await meeting.addMedia({
3028
3072
  mediaSettings: {},
@@ -3044,6 +3088,8 @@ describe('plugin-meetings', () => {
3044
3088
  someReachabilityMetric1: 'some value1',
3045
3089
  someReachabilityMetric2: 'some value2',
3046
3090
  iceCandidatesCount: 3,
3091
+ '701_error': 3,
3092
+ '701_turn_host_lookup_received_error': 1,
3047
3093
  }
3048
3094
  );
3049
3095
 
@@ -3397,9 +3443,7 @@ describe('plugin-meetings', () => {
3397
3443
  Media.createMediaConnection,
3398
3444
  false,
3399
3445
  meeting.getMediaConnectionDebugId(),
3400
- webex,
3401
3446
  meeting.id,
3402
- meeting.correlationId,
3403
3447
  sinon.match({
3404
3448
  turnServerInfo: {
3405
3449
  url: FAKE_TURN_URL,
@@ -4240,6 +4284,20 @@ describe('plugin-meetings', () => {
4240
4284
  assert.calledTwice(locusMediaRequestStub);
4241
4285
  });
4242
4286
 
4287
+ it('addMedia() works correctly when media is disabled with no streams to publish', async () => {
4288
+ const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
4289
+ await meeting.addMedia({audioEnabled: false});
4290
+ //calling handleDeviceLogging with audioEnaled as true adn videoEnabled as false
4291
+ assert.calledWith(handleDeviceLoggingSpy,false,true);
4292
+ });
4293
+
4294
+ it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
4295
+ const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
4296
+ await meeting.addMedia({videoEnabled: false});
4297
+ //calling handleDeviceLogging audioEnabled as true videoEnabled as false
4298
+ assert.calledWith(handleDeviceLoggingSpy,true,false);
4299
+ });
4300
+
4243
4301
  it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
4244
4302
  await meeting.addMedia({videoEnabled: false});
4245
4303
  await simulateRoapOffer();
@@ -4306,6 +4364,14 @@ describe('plugin-meetings', () => {
4306
4364
  assert.calledTwice(locusMediaRequestStub);
4307
4365
  });
4308
4366
 
4367
+
4368
+ it('addMedia() works correctly when both shareAudio and shareVideo is disabled with no streams publish', async () => {
4369
+ const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
4370
+ await meeting.addMedia({shareAudioEnabled: false, shareVideoEnabled: false});
4371
+ //calling handleDeviceLogging with audioEnabled true and videoEnabled as true
4372
+ assert.calledWith(handleDeviceLoggingSpy,true,true);
4373
+ });
4374
+
4309
4375
  describe('publishStreams()/unpublishStreams() calls', () => {
4310
4376
  [
4311
4377
  {mediaEnabled: true, expected: {direction: 'sendrecv', localMuteSentValue: false}},
@@ -8450,6 +8516,9 @@ describe('plugin-meetings', () => {
8450
8516
  it('listens to the self admitted guest event', (done) => {
8451
8517
  meeting.stopKeepAlive = sinon.stub();
8452
8518
  meeting.updateLLMConnection = sinon.stub();
8519
+ meeting.rtcMetrics = {
8520
+ sendNextMetrics: sinon.stub(),
8521
+ };
8453
8522
  meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ADMITTED_GUEST', test1);
8454
8523
  assert.calledOnceWithExactly(meeting.stopKeepAlive);
8455
8524
  assert.calledThrice(TriggerProxy.trigger);
@@ -8461,6 +8530,8 @@ describe('plugin-meetings', () => {
8461
8530
  {payload: test1}
8462
8531
  );
8463
8532
  assert.calledOnce(meeting.updateLLMConnection);
8533
+ assert.calledOnceWithExactly(meeting.rtcMetrics.sendNextMetrics);
8534
+
8464
8535
  done();
8465
8536
  });
8466
8537
 
@@ -1,6 +1,7 @@
1
+ import 'jsdom-global/register';
1
2
  import sinon from 'sinon';
2
3
  import {assert} from '@webex/test-helper-chai';
3
- import { cloneDeep, defer } from 'lodash';
4
+ import { cloneDeep } from 'lodash';
4
5
 
5
6
  import MockWebex from '@webex/test-helper-mock-webex';
6
7
  import Meetings from '@webex/plugin-meetings';
@@ -495,4 +496,4 @@ describe('LocusMediaRequest.send()', () => {
495
496
  });
496
497
 
497
498
  });
498
- })
499
+ })
@@ -1,3 +1,4 @@
1
+ import 'jsdom-global/register';
1
2
  import sinon from 'sinon';
2
3
  import {assert} from '@webex/test-helper-chai';
3
4
  import MockWebex from '@webex/test-helper-mock-webex';
@@ -1,3 +1,4 @@
1
+ import 'jsdom-global/register';
1
2
  import sinon from 'sinon';
2
3
  import {assert} from '@webex/test-helper-chai';
3
4
  import Meetings from '@webex/plugin-meetings';
@@ -71,6 +72,7 @@ describe('plugin-meetings', () => {
71
72
  assert.calledOnce(meeting.updateLLMConnection);
72
73
  assert.calledOnce(meeting.breakouts.cleanUp);
73
74
  assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
75
+ assert.calledOnce(webex.internal.device.meetingEnded);
74
76
  });
75
77
 
76
78
  it('do clean up on meeting object with LLM disabled', async () => {
@@ -87,6 +89,7 @@ describe('plugin-meetings', () => {
87
89
  assert.notCalled(meeting.updateLLMConnection);
88
90
  assert.calledOnce(meeting.breakouts.cleanUp);
89
91
  assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
92
+ assert.calledOnce(webex.internal.device.meetingEnded);
90
93
  });
91
94
 
92
95
  it('do clean up on meeting object with no config', async () => {
@@ -102,6 +105,7 @@ describe('plugin-meetings', () => {
102
105
  assert.notCalled(meeting.updateLLMConnection);
103
106
  assert.calledOnce(meeting.breakouts.cleanUp);
104
107
  assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
108
+ assert.calledOnce(webex.internal.device.meetingEnded);
105
109
  });
106
110
  });
107
111
 
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
3
  */
4
-
4
+ import 'jsdom-global/register';
5
5
  import {assert} from '@webex/test-helper-chai';
6
6
  import sinon from 'sinon';
7
7
  import MockWebex from '@webex/test-helper-mock-webex';
@@ -23,7 +23,6 @@ import MeetingInfoUtil from '@webex/plugin-meetings/src/meeting-info/utilv2';
23
23
  import Metrics from '@webex/plugin-meetings/src/metrics';
24
24
  import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
25
25
  import {forEach} from 'lodash';
26
- import { request } from 'express';
27
26
 
28
27
  describe('plugin-meetings', () => {
29
28
  const conversation = {
@@ -433,7 +432,7 @@ describe('plugin-meetings', () => {
433
432
  assert.deepEqual(submitInternalEventCalls[1].args[0], {
434
433
  name: 'internal.client.meetinginfo.response',
435
434
  });
436
-
435
+
437
436
  assert.deepEqual(submitClientEventCalls[1].args[0], {
438
437
  name: 'client.meetinginfo.response',
439
438
  payload: {
@@ -484,9 +483,9 @@ describe('plugin-meetings', () => {
484
483
  requestResponse.body.confIdStr = confIdStr;
485
484
  }
486
485
  const extraParams = {mtid: 'm9fe0afd8c435e892afcce9ea25b97046', joinTXId: 'TSmrX61wNF'}
487
-
486
+
488
487
  webex.request.resolves(requestResponse);
489
-
488
+
490
489
  const result = await meetingInfo.fetchMeetingInfo(
491
490
  '1234323',
492
491
  DESTINATION_TYPE.MEETING_ID,
@@ -497,7 +496,7 @@ describe('plugin-meetings', () => {
497
496
  extraParams,
498
497
  {meetingId, sendCAevents}
499
498
  );
500
-
499
+
501
500
  assert.calledWith(webex.request, {
502
501
  method: 'POST',
503
502
  service: WBXAPPAPI_SERVICE,
@@ -515,7 +514,7 @@ describe('plugin-meetings', () => {
515
514
  Metrics.sendBehavioralMetric,
516
515
  BEHAVIORAL_METRICS.FETCH_MEETING_INFO_V1_SUCCESS
517
516
  );
518
-
517
+
519
518
  const submitInternalEventCalls = webex.internal.newMetrics.submitInternalEvent.getCalls();
520
519
  const submitClientEventCalls = webex.internal.newMetrics.submitClientEvent.getCalls();
521
520
 
@@ -529,7 +528,7 @@ describe('plugin-meetings', () => {
529
528
  meetingId,
530
529
  }
531
530
  });
532
-
531
+
533
532
  assert.deepEqual(submitInternalEventCalls[1].args[0], {
534
533
  name: 'internal.client.meetinginfo.response',
535
534
  });
@@ -591,7 +590,7 @@ describe('plugin-meetings', () => {
591
590
 
592
591
  const submitInternalEventCalls = webex.internal.newMetrics.submitInternalEvent.getCalls();
593
592
  const submitClientEventCalls = webex.internal.newMetrics.submitClientEvent.getCalls();
594
-
593
+
595
594
  assert.deepEqual(submitInternalEventCalls[0].args[0], {
596
595
  name: 'internal.client.meetinginfo.request',
597
596
  });
@@ -601,7 +600,7 @@ describe('plugin-meetings', () => {
601
600
  meetingId: 'meetingId',
602
601
  }
603
602
  });
604
-
603
+
605
604
  assert.deepEqual(submitInternalEventCalls[1].args[0], {
606
605
  name: 'internal.client.meetinginfo.response',
607
606
  });
@@ -629,7 +628,7 @@ describe('plugin-meetings', () => {
629
628
  it(`should not send CA metric if meetingId is not provided disregarding if sendCAevents is ${sendCAevents}`, async () => {
630
629
  const message = 'a message';
631
630
  const meetingInfoData = 'meeting info';
632
-
631
+
633
632
  webex.request = sinon.stub().rejects({
634
633
  statusCode: 403,
635
634
  body: {message, code: 403102, data: {meetingInfo: meetingInfoData}},
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
3
  */
4
-
4
+ import 'jsdom-global/register';
5
5
  import {assert} from '@webex/test-helper-chai';
6
6
  import sinon from 'sinon';
7
7
  import MockWebex from '@webex/test-helper-mock-webex';