@webex/plugin-meetings 3.0.0-beta.294 → 3.0.0-beta.296

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.
@@ -58,6 +58,7 @@ import * as MeetingRequestImport from '@webex/plugin-meetings/src/meeting/reques
58
58
  import LocusInfo from '@webex/plugin-meetings/src/locus-info';
59
59
  import MediaProperties from '@webex/plugin-meetings/src/media/properties';
60
60
  import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
61
+ import MeetingsUtil from '@webex/plugin-meetings/src/meetings/util';
61
62
  import Media from '@webex/plugin-meetings/src/media/index';
62
63
  import ReconnectionManager from '@webex/plugin-meetings/src/reconnection-manager';
63
64
  import MediaUtil from '@webex/plugin-meetings/src/media/util';
@@ -1193,7 +1194,7 @@ describe('plugin-meetings', () => {
1193
1194
 
1194
1195
  it('should send metrics and reset the statsAnalyzer to null if addMedia throws an error without a turn server retry', async () => {
1195
1196
  meeting.meetingState = 'ACTIVE';
1196
-
1197
+
1197
1198
  meeting.webex.meetings.reachability = {
1198
1199
  getReachabilityMetrics: sinon.stub().resolves({
1199
1200
  someReachabilityMetric1: 'some value1',
@@ -2361,7 +2362,7 @@ describe('plugin-meetings', () => {
2361
2362
  let clock;
2362
2363
 
2363
2364
  beforeEach(() => {
2364
- clock = sinon.useFakeTimers();
2365
+ clock = sinon.useFakeTimers();
2365
2366
 
2366
2367
  sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.unknown);
2367
2368
 
@@ -2501,7 +2502,7 @@ describe('plugin-meetings', () => {
2501
2502
 
2502
2503
  // simulates a Roap OK being sent
2503
2504
  const simulateRoapOk = async () => {
2504
- const roapListener = getRoapListener();
2505
+ const roapListener = getRoapListener();
2505
2506
 
2506
2507
  await roapListener({roapMessage: roapOKMessage});
2507
2508
  await stableState();
@@ -2523,7 +2524,7 @@ describe('plugin-meetings', () => {
2523
2524
  correlationId: meeting.correlationId,
2524
2525
  localMedias: [
2525
2526
  {
2526
- localSdp: `{"audioMuted":${audioMuted},"videoMuted":${videoMuted},"roapMessage":{"messageType":"OFFER","sdps":["${sdp}"],"version":"2","seq":"${seq}","tieBreaker":"${tieBreaker}"}}`,
2527
+ localSdp: `{"audioMuted":${audioMuted},"videoMuted":${videoMuted},"roapMessage":{"messageType":"OFFER","sdps":["${sdp}"],"version":"2","seq":"${seq}","tieBreaker":"${tieBreaker}","headers":["includeAnswerInHttpResponse","noOkInTransaction"]}}`,
2527
2528
  mediaId: 'fake media id',
2528
2529
  },
2529
2530
  ],
@@ -5746,34 +5747,20 @@ describe('plugin-meetings', () => {
5746
5747
  });
5747
5748
  });
5748
5749
 
5749
- describe('handles Event.ROAP_MESSAGE_TO_SEND correctly', () => {
5750
- let sendRoapOKStub;
5751
- let sendRoapMediaRequestStub;
5752
- let sendRoapAnswerStub;
5753
- let sendRoapErrorStub;
5754
-
5750
+ describe('handles SDP events correctly', () => {
5755
5751
  beforeEach(() => {
5756
- sendRoapOKStub = sinon.stub(meeting.roap, 'sendRoapOK').resolves({});
5757
- sendRoapMediaRequestStub = sinon
5758
- .stub(meeting.roap, 'sendRoapMediaRequest')
5759
- .resolves({});
5760
- sendRoapAnswerStub = sinon.stub(meeting.roap, 'sendRoapAnswer').resolves({});
5761
- sendRoapErrorStub = sinon.stub(meeting.roap, 'sendRoapError').resolves({});
5762
-
5763
5752
  meeting.setupMediaConnectionListeners();
5764
5753
  });
5765
5754
 
5766
- it('handles OK message correctly', () => {
5755
+ it('handles REMOTE_SDP_ANSWER_PROCESSED correctly', () => {
5767
5756
  const clock = sinon.useFakeTimers();
5768
- sinon.spy(clock, "clearTimeout");
5757
+ sinon.spy(clock, 'clearTimeout');
5769
5758
  meeting.deferSDPAnswer = {
5770
5759
  resolve: sinon.stub(),
5771
5760
  };
5772
5761
  meeting.sdpResponseTimer = '1234';
5773
5762
 
5774
- eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
5775
- roapMessage: {messageType: 'OK', seq: 1},
5776
- });
5763
+ eventListeners[Event.REMOTE_SDP_ANSWER_PROCESSED]();
5777
5764
 
5778
5765
  assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
5779
5766
  assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
@@ -5781,13 +5768,6 @@ describe('plugin-meetings', () => {
5781
5768
  options: {meetingId: meeting.id},
5782
5769
  });
5783
5770
 
5784
- assert.calledOnce(sendRoapOKStub);
5785
- assert.calledWith(sendRoapOKStub, {
5786
- seq: 1,
5787
- mediaId: meeting.mediaId,
5788
- correlationId: meeting.correlationId,
5789
- });
5790
-
5791
5771
  assert.calledOnce(Metrics.sendBehavioralMetric);
5792
5772
  assert.calledWith(
5793
5773
  Metrics.sendBehavioralMetric,
@@ -5799,15 +5779,70 @@ describe('plugin-meetings', () => {
5799
5779
  }
5800
5780
  );
5801
5781
 
5802
- assert.calledOnce(meeting.deferSDPAnswer.resolve)
5803
- assert.calledOnce(clock.clearTimeout)
5804
- assert.calledWith(clock.clearTimeout, '1234')
5805
- assert.equal(meeting.sdpResponseTimer, undefined)
5782
+ assert.calledOnce(meeting.deferSDPAnswer.resolve);
5783
+ assert.calledOnce(clock.clearTimeout);
5784
+ assert.calledWith(clock.clearTimeout, '1234');
5785
+ assert.equal(meeting.sdpResponseTimer, undefined);
5806
5786
  });
5807
5787
 
5808
- it('handles OFFER message correctly', () => {
5788
+ it('handles LOCAL_SDP_OFFER_GENERATED correctly', () => {
5809
5789
  assert.equal(meeting.deferSDPAnswer, undefined);
5810
5790
 
5791
+ eventListeners[Event.LOCAL_SDP_OFFER_GENERATED]();
5792
+
5793
+ assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
5794
+ assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
5795
+ name: 'client.media-engine.local-sdp-generated',
5796
+ options: {meetingId: meeting.id},
5797
+ });
5798
+
5799
+ assert.notEqual(meeting.deferSDPAnswer, undefined);
5800
+ });
5801
+
5802
+ it('handles LOCAL_SDP_ANSWER_GENERATED correctly', () => {
5803
+ eventListeners[Event.LOCAL_SDP_ANSWER_GENERATED]();
5804
+
5805
+ assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
5806
+ assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
5807
+ name: 'client.media-engine.remote-sdp-received',
5808
+ options: {meetingId: meeting.id},
5809
+ });
5810
+ });
5811
+ });
5812
+
5813
+ describe('handles Event.ROAP_MESSAGE_TO_SEND correctly', () => {
5814
+ let sendRoapOKStub;
5815
+ let sendRoapMediaRequestStub;
5816
+ let sendRoapAnswerStub;
5817
+ let sendRoapErrorStub;
5818
+
5819
+ beforeEach(() => {
5820
+ sendRoapOKStub = sinon.stub(meeting.roap, 'sendRoapOK').resolves({});
5821
+ sendRoapMediaRequestStub = sinon
5822
+ .stub(meeting.roap, 'sendRoapMediaRequest')
5823
+ .resolves({});
5824
+ sendRoapAnswerStub = sinon.stub(meeting.roap, 'sendRoapAnswer').resolves({});
5825
+ sendRoapErrorStub = sinon.stub(meeting.roap, 'sendRoapError').resolves({});
5826
+
5827
+ meeting.setupMediaConnectionListeners();
5828
+ });
5829
+
5830
+ it('handles OK message correctly', () => {
5831
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
5832
+ roapMessage: {messageType: 'OK', seq: 1},
5833
+ });
5834
+
5835
+ assert.calledOnce(sendRoapOKStub);
5836
+ assert.calledWith(sendRoapOKStub, {
5837
+ seq: 1,
5838
+ mediaId: meeting.mediaId,
5839
+ correlationId: meeting.correlationId,
5840
+ });
5841
+ });
5842
+
5843
+ it('handles OFFER message correctly (no answer in the http response)', async () => {
5844
+ sinon.stub(meeting, 'roapMessageReceived');
5845
+
5811
5846
  eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
5812
5847
  roapMessage: {
5813
5848
  messageType: 'OFFER',
@@ -5817,11 +5852,7 @@ describe('plugin-meetings', () => {
5817
5852
  },
5818
5853
  });
5819
5854
 
5820
- assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
5821
- assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
5822
- name: 'client.media-engine.local-sdp-generated',
5823
- options: {meetingId: meeting.id},
5824
- });
5855
+ await testUtils.flushPromises();
5825
5856
 
5826
5857
  assert.calledOnce(sendRoapMediaRequestStub);
5827
5858
  assert.calledWith(sendRoapMediaRequestStub, {
@@ -5831,8 +5862,34 @@ describe('plugin-meetings', () => {
5831
5862
  meeting,
5832
5863
  reconnect: false,
5833
5864
  });
5865
+ assert.notCalled(meeting.roapMessageReceived);
5866
+ });
5834
5867
 
5835
- assert.notEqual(meeting.deferSDPAnswer, undefined);
5868
+ it('handles OFFER message correctly (with an answer in the http response)', async () => {
5869
+ const fakeAnswer = {messageType: 'answer', sdp: 'sdp'};
5870
+ sendRoapMediaRequestStub.resolves({roapAnswer: fakeAnswer});
5871
+ sinon.stub(meeting, 'roapMessageReceived');
5872
+
5873
+ eventListeners[Event.ROAP_MESSAGE_TO_SEND]({
5874
+ roapMessage: {
5875
+ messageType: 'OFFER',
5876
+ seq: 1,
5877
+ sdp: 'fake sdp',
5878
+ tieBreaker: 12345,
5879
+ },
5880
+ });
5881
+
5882
+ await testUtils.flushPromises();
5883
+
5884
+ assert.calledOnce(sendRoapMediaRequestStub);
5885
+ assert.calledWith(sendRoapMediaRequestStub, {
5886
+ seq: 1,
5887
+ sdp: 'fake sdp',
5888
+ tieBreaker: 12345,
5889
+ meeting,
5890
+ reconnect: false,
5891
+ });
5892
+ assert.calledWith(meeting.roapMessageReceived, fakeAnswer);
5836
5893
  });
5837
5894
 
5838
5895
  it('handles ANSWER message correctly', () => {
@@ -5845,12 +5902,6 @@ describe('plugin-meetings', () => {
5845
5902
  },
5846
5903
  });
5847
5904
 
5848
- assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
5849
- assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
5850
- name: 'client.media-engine.remote-sdp-received',
5851
- options: {meetingId: meeting.id},
5852
- });
5853
-
5854
5905
  assert.calledOnce(sendRoapAnswerStub);
5855
5906
  assert.calledWith(sendRoapAnswerStub, {
5856
5907
  seq: 10,
@@ -9300,4 +9351,22 @@ describe('plugin-meetings', () => {
9300
9351
  assert.equal(returnValue, undefined);
9301
9352
  });
9302
9353
  });
9354
+
9355
+ describe('#roapMessageReceived', () => {
9356
+ it('calls roapMessageReceived on the webrtc media connection', () => {
9357
+ const fakeMessage = { messageType: 'fake', sdp: 'fake sdp'};
9358
+
9359
+ const getMediaServer = sinon.stub(MeetingsUtil, 'getMediaServer').returns('homer');
9360
+
9361
+ meeting.mediaProperties.webrtcMediaConnection = {
9362
+ roapMessageReceived: sinon.stub()
9363
+ };
9364
+
9365
+ meeting.roapMessageReceived(fakeMessage);
9366
+
9367
+ assert.calledOnceWithExactly(meeting.mediaProperties.webrtcMediaConnection.roapMessageReceived, fakeMessage);
9368
+ assert.calledOnceWithExactly(getMediaServer, 'fake sdp');
9369
+ assert.equal(meeting.mediaProperties.webrtcMediaConnection.mediaServer, 'homer');
9370
+ })
9371
+ })
9303
9372
  });
@@ -181,11 +181,7 @@ describe('plugin-meetings', () => {
181
181
  const meetingCollection = {
182
182
  getByKey: () => ({
183
183
  id: 'meeting-id',
184
- mediaProperties: {
185
- webrtcMediaConnection: {
186
- roapMessageReceived
187
- }
188
- }
184
+ roapMessageReceived
189
185
  })
190
186
  };
191
187
 
@@ -7,6 +7,8 @@ import RoapRequest from '@webex/plugin-meetings/src/roap/request';
7
7
  import Roap from '@webex/plugin-meetings/src/roap/';
8
8
  import Meeting from '@webex/plugin-meetings/src/meeting';
9
9
  import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
10
+ import Metrics from '@webex/plugin-meetings/src/metrics';
11
+ import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
10
12
 
11
13
  import { IP_VERSION } from '../../../../src/constants';
12
14
 
@@ -42,6 +44,9 @@ describe('Roap', () => {
42
44
  let meeting;
43
45
 
44
46
  let webex;
47
+ let roap;
48
+
49
+ const fakeLocus = {id: 'fake locus'};
45
50
 
46
51
  beforeEach(() => {
47
52
  webex = new MockWebex({});
@@ -50,21 +55,27 @@ describe('Roap', () => {
50
55
  correlationId: 'correlation id',
51
56
  selfUrl: 'self url',
52
57
  mediaId: 'media id',
53
- audio:{
58
+ audio: {
54
59
  isLocallyMuted: () => true,
55
60
  },
56
- video:{
61
+ video: {
57
62
  isLocallyMuted: () => false,
58
63
  },
64
+ isMultistream: true,
59
65
  setRoapSeq: sinon.stub(),
60
66
  locusMediaRequest: {fake: true},
61
- webex: { meetings: { reachability: { isAnyPublicClusterReachable: () => true}}},
67
+ webex: {meetings: {reachability: {isAnyPublicClusterReachable: () => true}}},
68
+ updateMediaConnections: sinon.stub(),
62
69
  };
63
70
 
64
71
  sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.unknown);
72
+ sinon.stub(Metrics, 'sendBehavioralMetric');
65
73
 
66
74
  sendRoapStub = sinon.stub(RoapRequest.prototype, 'sendRoap').resolves({});
67
75
  meeting.setRoapSeq.resetHistory();
76
+
77
+ roap = new Roap({}, {parent: webex});
78
+ sinon.stub(roap.turnDiscovery, 'isSkipped').resolves(false);
68
79
  });
69
80
 
70
81
  afterEach(() => {
@@ -82,9 +93,7 @@ describe('Roap', () => {
82
93
  }reconnecting and TURN discovery is ${
83
94
  turnDiscoverySkipped ? 'skipped' : 'not skipped'
84
95
  }`, async () => {
85
- const roap = new Roap({}, {parent: webex});
86
-
87
- sinon.stub(roap.turnDiscovery, 'isSkipped').resolves(turnDiscoverySkipped);
96
+ roap.turnDiscovery.isSkipped.resolves(turnDiscoverySkipped);
88
97
 
89
98
  await roap.sendRoapMediaRequest({
90
99
  meeting,
@@ -100,17 +109,148 @@ describe('Roap', () => {
100
109
  version: '2',
101
110
  seq: 2,
102
111
  tieBreaker: 4294967294,
112
+ headers: ['includeAnswerInHttpResponse', 'noOkInTransaction'],
103
113
  };
104
114
 
105
115
  assert.calledOnce(sendRoapStub);
106
- assert.calledWith(sendRoapStub, sinon.match({
107
- roapMessage: expectedRoapMessage,
108
- locusSelfUrl: meeting.selfUrl,
109
- mediaId: expectEmptyMediaId ? '' : meeting.mediaId,
110
- meetingId: meeting.id,
111
- locusMediaRequest: meeting.locusMediaRequest,
112
- }));
116
+ assert.calledWith(
117
+ sendRoapStub,
118
+ sinon.match({
119
+ roapMessage: expectedRoapMessage,
120
+ locusSelfUrl: meeting.selfUrl,
121
+ mediaId: expectEmptyMediaId ? '' : meeting.mediaId,
122
+ meetingId: meeting.id,
123
+ locusMediaRequest: meeting.locusMediaRequest,
124
+ })
125
+ );
113
126
  })
114
127
  );
128
+
129
+ it('reads SDP answer from the http response', async () => {
130
+ const roapAnswer = {
131
+ seq: 5,
132
+ messageType: 'ANSWER',
133
+ sdps: ['sdp answer'],
134
+ errorType: 'error type', // normally ANSWER would not have errorType or errorCause (only error messages have these)
135
+ errorCause: 'error cause', // but we're just testing here that all the fields are forwarded to the caller of sendRoapMediaRequest()
136
+ headers: ['header1', 'header2'],
137
+ };
138
+ const fakeMediaConnections = [
139
+ {
140
+ remoteSdp: JSON.stringify({
141
+ roapMessage: roapAnswer,
142
+ }),
143
+ },
144
+ ];
145
+
146
+ sendRoapStub.resolves({
147
+ mediaConnections: fakeMediaConnections,
148
+ locus: fakeLocus,
149
+ });
150
+
151
+ const result = await roap.sendRoapMediaRequest({
152
+ meeting,
153
+ sdp: 'sdp',
154
+ reconnect: false,
155
+ seq: 1,
156
+ tieBreaker: 4294967294,
157
+ });
158
+
159
+ assert.calledOnce(sendRoapStub);
160
+ assert.calledOnceWithExactly(meeting.updateMediaConnections, fakeMediaConnections);
161
+ assert.deepEqual(result, {
162
+ locus: fakeLocus,
163
+ roapAnswer: {
164
+ seq: 5,
165
+ messageType: 'ANSWER',
166
+ sdp: 'sdp answer',
167
+ errorType: 'error type',
168
+ errorCause: 'error cause',
169
+ headers: ['header1', 'header2'],
170
+ },
171
+ });
172
+ });
173
+
174
+ it('handles the case when there is no answer in the http response', async () => {
175
+ const fakeMediaConnections = [
176
+ {
177
+ // this is the actual value Locus returns to us when they don't send Roap ANSWER in the http response
178
+ remoteSdp:
179
+ '{"audioMuted":false,"videoMuted":false,"csis":[],"dtmfReceiveSupported":true,"type":"SDP"}',
180
+ },
181
+ ];
182
+
183
+ sendRoapStub.resolves({
184
+ mediaConnections: fakeMediaConnections,
185
+ locus: fakeLocus,
186
+ });
187
+
188
+ const result = await roap.sendRoapMediaRequest({
189
+ meeting,
190
+ sdp: 'sdp',
191
+ reconnect: false,
192
+ seq: 1,
193
+ tieBreaker: 4294967294,
194
+ });
195
+
196
+ assert.calledOnce(sendRoapStub);
197
+ assert.calledOnceWithExactly(meeting.updateMediaConnections, fakeMediaConnections);
198
+ assert.deepEqual(result, {
199
+ locus: fakeLocus,
200
+ roapAnswer: undefined,
201
+ });
202
+ assert.calledOnceWithExactly(
203
+ Metrics.sendBehavioralMetric,
204
+ BEHAVIORAL_METRICS.ROAP_HTTP_RESPONSE_MISSING,
205
+ {
206
+ correlationId: meeting.correlationId,
207
+ messageType: 'ANSWER',
208
+ isMultistream: meeting.isMultistream,
209
+ }
210
+ );
211
+ });
212
+
213
+ describe('does not crash when http response is missing things', () => {
214
+ [
215
+ {mediaConnections: undefined, title: 'mediaConnections are undefined'},
216
+ {mediaConnections: [], title: 'mediaConnections are empty array'},
217
+ {mediaConnections: [{}], title: 'mediaConnections[0] has no remoteSdp'},
218
+ {
219
+ mediaConnections: [{remoteSdp: '{}'}],
220
+ title: 'mediaConnections[0].remoteSdp is an empty json',
221
+ },
222
+ ].forEach(({mediaConnections, title}) =>
223
+ it(title, async () => {
224
+ sendRoapStub.resolves({
225
+ mediaConnections,
226
+ locus: fakeLocus,
227
+ });
228
+
229
+ const result = await roap.sendRoapMediaRequest({
230
+ meeting,
231
+ sdp: 'sdp',
232
+ reconnect: false,
233
+ seq: 1,
234
+ tieBreaker: 4294967294,
235
+ });
236
+
237
+ assert.calledOnce(sendRoapStub);
238
+ assert.deepEqual(result, {
239
+ locus: fakeLocus,
240
+ roapAnswer: undefined,
241
+ });
242
+
243
+ assert.calledOnceWithExactly(
244
+ Metrics.sendBehavioralMetric,
245
+ BEHAVIORAL_METRICS.ROAP_HTTP_RESPONSE_MISSING,
246
+ {
247
+ correlationId: meeting.correlationId,
248
+ messageType: 'ANSWER',
249
+ isMultistream: meeting.isMultistream,
250
+ }
251
+ );
252
+ })
253
+ );
254
+ });
115
255
  });
116
256
  });