@webex/plugin-meetings 2.60.1-next.13 → 2.60.1-next.14

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 (57) 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/mediaQualityMetrics/config.d.ts +103 -99
  6. package/dist/mediaQualityMetrics/config.js +133 -129
  7. package/dist/mediaQualityMetrics/config.js.map +1 -1
  8. package/dist/meeting/index.js +4 -2
  9. package/dist/meeting/index.js.map +1 -1
  10. package/dist/meeting/request.d.ts +2 -0
  11. package/dist/meeting/request.js +4 -0
  12. package/dist/meeting/request.js.map +1 -1
  13. package/dist/meetings/index.js +19 -0
  14. package/dist/meetings/index.js.map +1 -1
  15. package/dist/meetings/util.js +1 -1
  16. package/dist/meetings/util.js.map +1 -1
  17. package/dist/metrics/constants.d.ts +2 -0
  18. package/dist/metrics/constants.js +3 -1
  19. package/dist/metrics/constants.js.map +1 -1
  20. package/dist/reachability/index.js +14 -20
  21. package/dist/reachability/index.js.map +1 -1
  22. package/dist/reconnection-manager/index.js +63 -43
  23. package/dist/reconnection-manager/index.js.map +1 -1
  24. package/dist/roap/turnDiscovery.d.ts +18 -2
  25. package/dist/roap/turnDiscovery.js +163 -69
  26. package/dist/roap/turnDiscovery.js.map +1 -1
  27. package/dist/rtcMetrics/index.d.ts +7 -0
  28. package/dist/rtcMetrics/index.js +38 -1
  29. package/dist/rtcMetrics/index.js.map +1 -1
  30. package/dist/statsAnalyzer/index.js +135 -23
  31. package/dist/statsAnalyzer/index.js.map +1 -1
  32. package/dist/statsAnalyzer/mqaUtil.d.ts +28 -4
  33. package/dist/statsAnalyzer/mqaUtil.js +278 -148
  34. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  35. package/dist/webinar/index.js +1 -1
  36. package/package.json +21 -21
  37. package/src/mediaQualityMetrics/config.ts +107 -107
  38. package/src/meeting/index.ts +2 -0
  39. package/src/meeting/request.ts +6 -0
  40. package/src/meetings/index.ts +22 -0
  41. package/src/meetings/util.ts +1 -1
  42. package/src/metrics/constants.ts +2 -0
  43. package/src/reachability/index.ts +0 -6
  44. package/src/reconnection-manager/index.ts +18 -7
  45. package/src/roap/turnDiscovery.ts +100 -24
  46. package/src/rtcMetrics/index.ts +43 -1
  47. package/src/statsAnalyzer/index.ts +158 -24
  48. package/src/statsAnalyzer/mqaUtil.ts +302 -154
  49. package/test/unit/spec/meeting/index.js +46 -0
  50. package/test/unit/spec/meeting/request.js +2 -0
  51. package/test/unit/spec/meetings/utils.js +35 -8
  52. package/test/unit/spec/reachability/index.ts +74 -0
  53. package/test/unit/spec/reconnection-manager/index.js +36 -1
  54. package/test/unit/spec/roap/turnDiscovery.ts +326 -76
  55. package/test/unit/spec/rtcMetrics/index.ts +32 -3
  56. package/test/unit/spec/stats-analyzer/index.js +439 -1
  57. package/test/utils/webex-test-users.js +12 -4
@@ -3853,6 +3853,7 @@ describe('plugin-meetings', () => {
3853
3853
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
3854
3854
  meeting.mediaProperties.shareVideoStream = {};
3855
3855
  meeting.mediaProperties.mediaDirection.sendShare = true;
3856
+ meeting.deviceUrl = 'deviceUrl.com';
3856
3857
  meeting.state = 'JOINED';
3857
3858
  meeting.localShareInstanceId = '1234-5678';
3858
3859
  });
@@ -3868,6 +3869,15 @@ describe('plugin-meetings', () => {
3868
3869
  await share;
3869
3870
  assert.calledOnce(meeting.meetingRequest.changeMeetingFloor);
3870
3871
 
3872
+ assert.calledWith(meeting.meetingRequest.changeMeetingFloor, {
3873
+ disposition: FLOOR_ACTION.GRANTED,
3874
+ personUrl: url1,
3875
+ deviceUrl: 'deviceUrl.com',
3876
+ uri: url1,
3877
+ resourceUrl: undefined,
3878
+ shareInstanceId: '1234-5678',
3879
+ });
3880
+
3871
3881
  assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
3872
3882
  name: 'client.share.floor-grant.request',
3873
3883
  payload: {mediaType: 'share', shareInstanceId: '1234-5678'},
@@ -3887,6 +3897,15 @@ describe('plugin-meetings', () => {
3887
3897
  assert.equal(err, error);
3888
3898
  });
3889
3899
 
3900
+ assert.calledWith(meeting.meetingRequest.changeMeetingFloor, {
3901
+ disposition: 'GRANTED',
3902
+ personUrl: url1,
3903
+ deviceUrl: 'deviceUrl.com',
3904
+ uri: url1,
3905
+ resourceUrl: undefined,
3906
+ shareInstanceId: '1234-5678',
3907
+ });
3908
+
3890
3909
  assert.calledWith(getChangeMeetingFloorErrorPayloadSpy, 'forced');
3891
3910
 
3892
3911
  // ensure the expected CA share metric is submitted
@@ -7751,11 +7770,21 @@ describe('plugin-meetings', () => {
7751
7770
  meeting.locusInfo.self = {url: url2};
7752
7771
  meeting.mediaProperties = {mediaDirection: {sendShare: true}};
7753
7772
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
7773
+ (meeting.deviceUrl = 'deviceUrl.com'), (meeting.localShareInstanceId = '1234-5678');
7754
7774
  });
7755
7775
  it('should call changeMeetingFloor()', async () => {
7756
7776
  meeting.screenShareFloorState = 'GRANTED';
7757
7777
  const share = meeting.releaseScreenShareFloor();
7758
7778
 
7779
+ assert.calledWith(meeting.meetingRequest.changeMeetingFloor, {
7780
+ disposition: FLOOR_ACTION.RELEASED,
7781
+ personUrl: url2,
7782
+ deviceUrl: 'deviceUrl.com',
7783
+ uri: url1,
7784
+ resourceUrl: undefined,
7785
+ shareInstanceId: '1234-5678',
7786
+ });
7787
+
7759
7788
  assert.exists(share.then);
7760
7789
  await share;
7761
7790
  assert.calledOnce(meeting.meetingRequest.changeMeetingFloor);
@@ -9120,6 +9149,7 @@ describe('plugin-meetings', () => {
9120
9149
  meeting.locusInfo.mediaShares = [{name: 'whiteboard', url: url1}];
9121
9150
  meeting.locusInfo.self = {url: url1};
9122
9151
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
9152
+ meeting.deviceUrl = 'deviceUrl.com';
9123
9153
  });
9124
9154
  it('should have #startWhiteboardShare', () => {
9125
9155
  assert.exists(meeting.startWhiteboardShare);
@@ -9133,6 +9163,14 @@ describe('plugin-meetings', () => {
9133
9163
  await whiteboardShare;
9134
9164
  assert.calledOnce(meeting.meetingRequest.changeMeetingFloor);
9135
9165
 
9166
+ assert.calledWith(meeting.meetingRequest.changeMeetingFloor, {
9167
+ disposition: FLOOR_ACTION.GRANTED,
9168
+ personUrl: url1,
9169
+ deviceUrl: 'deviceUrl.com',
9170
+ uri: url1,
9171
+ resourceUrl: {channelUrl: url2},
9172
+ });
9173
+
9136
9174
  // ensure the CA share metric is submitted
9137
9175
  assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
9138
9176
  name: 'client.share.initiated',
@@ -9149,12 +9187,20 @@ describe('plugin-meetings', () => {
9149
9187
  meeting.locusInfo.mediaShares = [{name: 'whiteboard', url: url1}];
9150
9188
  meeting.locusInfo.self = {url: url1};
9151
9189
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
9190
+ meeting.deviceUrl = 'deviceUrl.com';
9152
9191
  });
9153
9192
  it('should stop the whiteboard share', async () => {
9154
9193
  const whiteboardShare = meeting.stopWhiteboardShare();
9155
9194
 
9156
9195
  assert.exists(whiteboardShare.then);
9157
9196
  await whiteboardShare;
9197
+
9198
+ assert.calledWith(meeting.meetingRequest.changeMeetingFloor, {
9199
+ disposition: FLOOR_ACTION.RELEASED,
9200
+ personUrl: url1,
9201
+ deviceUrl: 'deviceUrl.com',
9202
+ uri: url1,
9203
+ });
9158
9204
  assert.calledOnce(meeting.meetingRequest.changeMeetingFloor);
9159
9205
  });
9160
9206
  });
@@ -755,6 +755,7 @@ describe('plugin-meetings', () => {
755
755
  deviceUrl: 'deviceUrl',
756
756
  resourceId: 'resourceId',
757
757
  resourceUrl: 'resourceUrl',
758
+ shareInstanceId: '12345',
758
759
  uri: 'optionsUrl',
759
760
  annotationInfo: {
760
761
  version: '1',
@@ -768,6 +769,7 @@ describe('plugin-meetings', () => {
768
769
  version: '1',
769
770
  },
770
771
  floor: {
772
+ shareInstanceId: '12345',
771
773
  beneficiary: {
772
774
  devices: [
773
775
  {
@@ -162,9 +162,24 @@ describe('plugin-meetings', () => {
162
162
  });
163
163
 
164
164
  describe("#handleRoapMercury", () => {
165
- it('it sends the correct behaviour metric', () => {
166
- const roapMessageReceived = sinon.stub();
167
- const envelope = {
165
+ let envelope;
166
+ let meetingCollection;
167
+ let roapMessageReceived;
168
+ let handleTurnDiscoveryResponse;
169
+ let meeting;
170
+
171
+ beforeEach(() => {
172
+ roapMessageReceived = sinon.stub();
173
+ handleTurnDiscoveryResponse = sinon.stub();
174
+
175
+ meeting = {
176
+ id: 'meeting-id',
177
+ roapMessageReceived,
178
+ roap: {
179
+ turnDiscovery: {handleTurnDiscoveryResponse}
180
+ }
181
+ };
182
+ envelope = {
168
183
  data: {
169
184
  message:{
170
185
  seq: "seq",
@@ -178,13 +193,12 @@ describe('plugin-meetings', () => {
178
193
  eventType: 'locus.message.roap',
179
194
  }
180
195
  };
181
- const meetingCollection = {
182
- getByKey: () => ({
183
- id: 'meeting-id',
184
- roapMessageReceived
185
- })
196
+ meetingCollection = {
197
+ getByKey: () => meeting
186
198
  };
199
+ });
187
200
 
201
+ it('it sends the correct behaviour metric', () => {
188
202
  MeetingsUtil.handleRoapMercury(envelope, meetingCollection);
189
203
  assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ROAP_MERCURY_EVENT_RECEIVED, {
190
204
  correlation_id: 'correlationId',
@@ -203,6 +217,19 @@ describe('plugin-meetings', () => {
203
217
  })
204
218
 
205
219
  });
220
+
221
+ it('calls handleTurnDiscoveryResponse for TURN_DISCOVERY_RESPONSE', () => {
222
+ envelope.data.message.messageType = 'TURN_DISCOVERY_RESPONSE';
223
+ delete envelope.data.message.sdps;
224
+ MeetingsUtil.handleRoapMercury(envelope, meetingCollection);
225
+ assert.calledWith(meeting.roap.turnDiscovery.handleTurnDiscoveryResponse, {
226
+ seq: "seq",
227
+ messageType: 'TURN_DISCOVERY_RESPONSE',
228
+ errorType: 'errorType',
229
+ tieBreaker: 'tieBreaker',
230
+ errorCause: 'errorCause',
231
+ }, 'from mercury')
232
+ });
206
233
  })
207
234
  });
208
235
 
@@ -128,6 +128,11 @@ describe('gatherReachability', () => {
128
128
  'reachability.result',
129
129
  JSON.stringify({old: 'results'})
130
130
  );
131
+ await webex.boundedStorage.put(
132
+ 'Reachability',
133
+ 'reachability.joinCookie',
134
+ JSON.stringify({old: 'joinCookie'})
135
+ );
131
136
  });
132
137
 
133
138
  afterEach(() => {
@@ -169,6 +174,75 @@ describe('gatherReachability', () => {
169
174
  assert.equal(JSON.stringify(getClustersResult.joinCookie), storedResultForJoinCookie);
170
175
  });
171
176
 
177
+ it('keeps the stored reachability from previous call to gatherReachability if getClusters fails', async () => {
178
+ const reachability = new Reachability(webex);
179
+
180
+ const reachabilityResults = {
181
+ clusters: {
182
+ clusterId: {
183
+ udp: 'testUDP',
184
+ },
185
+ },
186
+ };
187
+ const getClustersResult = {
188
+ clusters: {clusterId: 'cluster'},
189
+ joinCookie: {id: 'id'},
190
+ };
191
+
192
+ reachability.reachabilityRequest.getClusters = sinon.stub().throws();
193
+
194
+ const result = await reachability.gatherReachability();
195
+
196
+ assert.empty(result);
197
+
198
+ const storedResultForReachabilityResult = await webex.boundedStorage.get(
199
+ 'Reachability',
200
+ 'reachability.result'
201
+ );
202
+ const storedResultForJoinCookie = await webex.boundedStorage.get(
203
+ 'Reachability',
204
+ 'reachability.joinCookie'
205
+ );
206
+
207
+ assert.equal(JSON.stringify({old: 'results'}), storedResultForReachabilityResult);
208
+ assert.equal(JSON.stringify({old: 'joinCookie'}), storedResultForJoinCookie);
209
+ });
210
+
211
+ it('keeps the stored reachability from previous call to gatherReachability if performReachabilityChecks fails', async () => {
212
+ const reachability = new Reachability(webex);
213
+
214
+ const reachabilityResults = {
215
+ clusters: {
216
+ clusterId: {
217
+ udp: 'testUDP',
218
+ },
219
+ },
220
+ };
221
+ const getClustersResult = {
222
+ clusters: {clusterId: 'cluster'},
223
+ joinCookie: {id: 'id'},
224
+ };
225
+
226
+ reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
227
+ (reachability as any).performReachabilityChecks = sinon.stub().throws();
228
+
229
+ const result = await reachability.gatherReachability();
230
+
231
+ assert.empty(result);
232
+
233
+ const storedResultForReachabilityResult = await webex.boundedStorage.get(
234
+ 'Reachability',
235
+ 'reachability.result'
236
+ );
237
+ const storedResultForJoinCookie = await webex.boundedStorage.get(
238
+ 'Reachability',
239
+ 'reachability.joinCookie'
240
+ );
241
+
242
+ assert.equal(JSON.stringify({old: 'results'}), storedResultForReachabilityResult);
243
+ assert.equal(JSON.stringify({old: 'joinCookie'}), storedResultForJoinCookie);
244
+ });
245
+
172
246
  it('starts ClusterReachability on each media cluster', async () => {
173
247
  webex.config.meetings.experimental = {enableTcpReachability: true};
174
248
 
@@ -4,6 +4,8 @@ import chaiAsPromised from 'chai-as-promised';
4
4
  import sinon from 'sinon';
5
5
  import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
6
6
  import { RECONNECTION } from '../../../../src/constants';
7
+ import LoggerProxy from '../../../../src/common/logs/logger-proxy';
8
+ import LoggerConfig from '../../../../src/common/logs/logger-config';
7
9
 
8
10
  const {assert} = chai;
9
11
 
@@ -12,8 +14,16 @@ sinon.assert.expose(chai.assert, {prefix: ''});
12
14
 
13
15
  describe('plugin-meetings', () => {
14
16
  describe('ReconnectionManager.reconnect', () => {
17
+ const sandbox = sinon.createSandbox();
15
18
  let fakeMediaConnection;
16
19
  let fakeMeeting;
20
+ let loggerSpy;
21
+
22
+ before(() => {
23
+ LoggerConfig.set({ enable: false });
24
+ LoggerProxy.set();
25
+ loggerSpy = sandbox.spy(LoggerProxy.logger, 'info');
26
+ });
17
27
 
18
28
  beforeEach(() => {
19
29
  fakeMediaConnection = {
@@ -65,6 +75,7 @@ describe('plugin-meetings', () => {
65
75
  meetings: {
66
76
  getMeetingByType: sinon.stub().returns(true),
67
77
  syncMeetings: sinon.stub().resolves({}),
78
+ startReachability: sinon.stub().resolves({}),
68
79
  },
69
80
  internal: {
70
81
  newMetrics: {
@@ -75,6 +86,10 @@ describe('plugin-meetings', () => {
75
86
  };
76
87
  });
77
88
 
89
+ afterEach(() => {
90
+ sandbox.reset();
91
+ });
92
+
78
93
  it('syncs meetings if it is not an unverified guest', async () => {
79
94
  const rm = new ReconnectionManager(fakeMeeting);
80
95
 
@@ -94,6 +109,27 @@ describe('plugin-meetings', () => {
94
109
  assert.notCalled(rm.webex.meetings.syncMeetings);
95
110
  });
96
111
 
112
+ it('calls startReachability on reconnect', async () => {
113
+ const rm = new ReconnectionManager(fakeMeeting);
114
+
115
+ await rm.reconnect();
116
+
117
+ assert.calledOnce(rm.webex.meetings.startReachability);
118
+ });
119
+
120
+ it('continues with reconnection attempt if startReachability throws an error', async () => {
121
+ const reachabilityError = new Error();
122
+ fakeMeeting.webex.meetings.startReachability = sinon.stub().throws(reachabilityError);
123
+
124
+ const rm = new ReconnectionManager(fakeMeeting);
125
+
126
+ await rm.reconnect();
127
+
128
+ assert.calledOnce(rm.webex.meetings.startReachability);
129
+ assert.calledWith(loggerSpy, 'ReconnectionManager:index#reconnect --> Reachability failed, continuing with reconnection attempt, err: ', reachabilityError);
130
+ assert.calledWith(loggerSpy, 'ReconnectionManager:index#executeReconnection --> Attempting to reconnect to meeting.');
131
+ });
132
+
97
133
  it('uses correct TURN TLS information on the reconnection', async () => {
98
134
  const rm = new ReconnectionManager(fakeMeeting);
99
135
 
@@ -142,7 +178,6 @@ describe('plugin-meetings', () => {
142
178
  assert.calledOnce(fakeMeeting.mediaRequestManagers.video.commit);
143
179
  });
144
180
 
145
-
146
181
  it('sends the correct client event when reconnection fails', async () => {
147
182
  sinon.stub(ReconnectionManager.prototype, 'executeReconnection').rejects();
148
183
  fakeMeeting.isMultistream = true;