@webex/plugin-meetings 3.11.0-next.2 → 3.11.0-next.21

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 (64) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/hashTree/hashTree.js +18 -0
  4. package/dist/hashTree/hashTree.js.map +1 -1
  5. package/dist/hashTree/hashTreeParser.js +307 -139
  6. package/dist/hashTree/hashTreeParser.js.map +1 -1
  7. package/dist/hashTree/types.js +2 -1
  8. package/dist/hashTree/types.js.map +1 -1
  9. package/dist/hashTree/utils.js +10 -0
  10. package/dist/hashTree/utils.js.map +1 -1
  11. package/dist/interpretation/index.js +1 -1
  12. package/dist/interpretation/siLanguage.js +1 -1
  13. package/dist/locus-info/index.js +55 -42
  14. package/dist/locus-info/index.js.map +1 -1
  15. package/dist/media/MediaConnectionAwaiter.js +57 -1
  16. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  17. package/dist/media/properties.js +4 -2
  18. package/dist/media/properties.js.map +1 -1
  19. package/dist/meeting/index.js +33 -22
  20. package/dist/meeting/index.js.map +1 -1
  21. package/dist/meeting/util.js +108 -2
  22. package/dist/meeting/util.js.map +1 -1
  23. package/dist/meetings/index.js +76 -26
  24. package/dist/meetings/index.js.map +1 -1
  25. package/dist/metrics/constants.js +2 -1
  26. package/dist/metrics/constants.js.map +1 -1
  27. package/dist/multistream/mediaRequestManager.js +1 -1
  28. package/dist/multistream/mediaRequestManager.js.map +1 -1
  29. package/dist/reactions/reactions.type.js.map +1 -1
  30. package/dist/types/hashTree/hashTree.d.ts +7 -0
  31. package/dist/types/hashTree/hashTreeParser.d.ts +47 -12
  32. package/dist/types/hashTree/types.d.ts +1 -0
  33. package/dist/types/hashTree/utils.d.ts +6 -0
  34. package/dist/types/locus-info/index.d.ts +9 -2
  35. package/dist/types/media/MediaConnectionAwaiter.d.ts +10 -1
  36. package/dist/types/media/properties.d.ts +2 -1
  37. package/dist/types/meeting/index.d.ts +8 -5
  38. package/dist/types/meeting/util.d.ts +28 -0
  39. package/dist/types/meetings/index.d.ts +3 -1
  40. package/dist/types/metrics/constants.d.ts +1 -0
  41. package/dist/types/reactions/reactions.type.d.ts +1 -0
  42. package/dist/webinar/index.js +1 -1
  43. package/package.json +22 -22
  44. package/src/hashTree/hashTree.ts +17 -0
  45. package/src/hashTree/hashTreeParser.ts +294 -96
  46. package/src/hashTree/types.ts +1 -0
  47. package/src/hashTree/utils.ts +9 -0
  48. package/src/locus-info/index.ts +83 -35
  49. package/src/media/MediaConnectionAwaiter.ts +41 -1
  50. package/src/media/properties.ts +3 -1
  51. package/src/meeting/index.ts +24 -11
  52. package/src/meeting/util.ts +132 -1
  53. package/src/meetings/index.ts +93 -8
  54. package/src/metrics/constants.ts +1 -0
  55. package/src/multistream/mediaRequestManager.ts +1 -1
  56. package/src/reactions/reactions.type.ts +1 -0
  57. package/test/unit/spec/hashTree/hashTree.ts +66 -0
  58. package/test/unit/spec/hashTree/hashTreeParser.ts +942 -110
  59. package/test/unit/spec/locus-info/index.js +88 -17
  60. package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
  61. package/test/unit/spec/media/properties.ts +12 -3
  62. package/test/unit/spec/meeting/index.js +160 -2
  63. package/test/unit/spec/meeting/utils.js +294 -22
  64. package/test/unit/spec/meetings/index.js +594 -17
@@ -29,7 +29,8 @@ import {
29
29
  } from '../../../../src/constants';
30
30
 
31
31
  import {self, selfWithInactivity} from './selfConstant';
32
- import { MEETING_REMOVED_REASON } from '@webex/plugin-meetings/src/constants';
32
+ import {MEETING_REMOVED_REASON} from '@webex/plugin-meetings/src/constants';
33
+ import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
33
34
 
34
35
  describe('plugin-meetings', () => {
35
36
  describe('LocusInfo index', () => {
@@ -106,7 +107,7 @@ describe('plugin-meetings', () => {
106
107
  const createHashTreeMessage = (visibleDataSets) => ({
107
108
  locusStateElements: [
108
109
  {
109
- htMeta: {elementId: {type: 'self'}},
110
+ htMeta: {elementId: {type: 'metadata'}},
110
111
  data: {visibleDataSets},
111
112
  },
112
113
  ],
@@ -136,9 +137,13 @@ describe('plugin-meetings', () => {
136
137
  HashTreeParserStub,
137
138
  sinon.match({
138
139
  initialLocus: {
139
- locus: {self: {visibleDataSets}},
140
+ locus: null,
140
141
  dataSets: [],
141
142
  },
143
+ metadata: {
144
+ htMeta: hashTreeMessage.locusStateElements[0].htMeta,
145
+ visibleDataSets,
146
+ },
142
147
  webexRequest: sinon.match.func,
143
148
  locusInfoUpdateCallback: sinon.match.func,
144
149
  debugId: sinon.match.string,
@@ -169,11 +174,16 @@ describe('plugin-meetings', () => {
169
174
  const visibleDataSets = ['dataset1', 'dataset2'];
170
175
  const locus = createLocusWithVisibleDataSets(visibleDataSets);
171
176
  const dataSets = [{name: 'dataset1', url: 'http://dataset-url.com'}];
177
+ const metadata = {
178
+ htMeta: {elementId: {type: 'metadata'}},
179
+ visibleDataSets,
180
+ };
172
181
 
173
182
  await locusInfo.initialSetup({
174
183
  trigger: 'join-response',
175
184
  locus,
176
185
  dataSets,
186
+ metadata,
177
187
  });
178
188
 
179
189
  assert.calledOnceWithExactly(
@@ -183,6 +193,7 @@ describe('plugin-meetings', () => {
183
193
  locus,
184
194
  dataSets,
185
195
  },
196
+ metadata,
186
197
  webexRequest: sinon.match.func,
187
198
  locusInfoUpdateCallback: sinon.match.func,
188
199
  debugId: sinon.match.string,
@@ -220,12 +231,13 @@ describe('plugin-meetings', () => {
220
231
  HashTreeParserStub,
221
232
  sinon.match({
222
233
  initialLocus: {
223
- locus: {self: {visibleDataSets}},
234
+ locus: null,
224
235
  dataSets: [],
225
236
  },
226
237
  webexRequest: sinon.match.func,
227
238
  locusInfoUpdateCallback: sinon.match.func,
228
239
  debugId: sinon.match.string,
240
+ metadata: null,
229
241
  })
230
242
  );
231
243
  assert.calledOnceWithExactly(mockHashTreeParser.initializeFromGetLociResponse, locus);
@@ -249,6 +261,30 @@ describe('plugin-meetings', () => {
249
261
  assert.isTrue(locusInfo.emitChange);
250
262
  });
251
263
 
264
+ it('throws if called with "locus-message" and Metadata object without visibleDataSets', async () => {
265
+ const hashTreeMessage = {
266
+ locusStateElements: [
267
+ {
268
+ htMeta: {elementId: {type: 'Metadata'}},
269
+ data: {},
270
+ },
271
+ ],
272
+ dataSets: [{name: 'dataset1', url: 'test-url'}],
273
+ };
274
+ try {
275
+ await locusInfo.initialSetup({
276
+ trigger: 'locus-message',
277
+ hashTreeMessage,
278
+ });
279
+ assert.fail('should have thrown an error');
280
+ } catch (error) {
281
+ assert.equal(
282
+ error.message,
283
+ 'Metadata object with visibleDataSets is missing in the message'
284
+ );
285
+ }
286
+ });
287
+
252
288
  describe('should setup correct locusInfoUpdateCallback when creating HashTreeParser', () => {
253
289
  const OBJECTS_UPDATED = HashTreeParserModule.LocusInfoUpdateType.OBJECTS_UPDATED;
254
290
  const MEETING_ENDED = HashTreeParserModule.LocusInfoUpdateType.MEETING_ENDED;
@@ -265,8 +301,8 @@ describe('plugin-meetings', () => {
265
301
  hashTreeMessage: {
266
302
  locusStateElements: [
267
303
  {
268
- htMeta: {elementId: {type: 'self'}},
269
- data: {visibleDataSets: ['dataset1']},
304
+ htMeta: {elementId: {type: 'Metadata'}},
305
+ data: {visibleDataSets: [{name: 'dataset1', url: 'test-url'}]},
270
306
  },
271
307
  ],
272
308
  dataSets: [{name: 'dataset1', url: 'test-url'}],
@@ -1076,7 +1112,7 @@ describe('plugin-meetings', () => {
1076
1112
  it('should trigger the CONTROLS_POLLING_QA_CHANGED event when necessary', () => {
1077
1113
  locusInfo.controls = {};
1078
1114
  locusInfo.emitScoped = sinon.stub();
1079
- newControls.pollingQAControl = { enabled: true };
1115
+ newControls.pollingQAControl = {enabled: true};
1080
1116
  locusInfo.updateControls(newControls);
1081
1117
 
1082
1118
  assert.calledWith(
@@ -1631,7 +1667,6 @@ describe('plugin-meetings', () => {
1631
1667
  );
1632
1668
  });
1633
1669
 
1634
-
1635
1670
  it('should call with participant display name', () => {
1636
1671
  const failureParticipant = [
1637
1672
  {
@@ -1656,7 +1691,7 @@ describe('plugin-meetings', () => {
1656
1691
  displayName: 'Test User',
1657
1692
  }
1658
1693
  );
1659
- })
1694
+ });
1660
1695
  });
1661
1696
 
1662
1697
  describe('#updateSelf', () => {
@@ -2457,8 +2492,8 @@ describe('plugin-meetings', () => {
2457
2492
  {
2458
2493
  isInitializing: !self,
2459
2494
  }
2460
- );
2461
- });
2495
+ );
2496
+ });
2462
2497
 
2463
2498
  const checkMeetingInfoUpdatedCalled = (expected, payload) => {
2464
2499
  const expectedArgs = [
@@ -3039,7 +3074,7 @@ describe('plugin-meetings', () => {
3039
3074
  sandbox.stub(locusInfo, 'handleOneOnOneEvent');
3040
3075
  sandbox.stub(locusParser, 'isNewFullLocus').returns(true);
3041
3076
 
3042
- locusInfo.onFullLocus(fakeLocus, eventType);
3077
+ locusInfo.onFullLocus('test', fakeLocus, eventType);
3043
3078
 
3044
3079
  assert.equal(fakeLocus, locusParser.workingCopy);
3045
3080
  });
@@ -3060,7 +3095,7 @@ describe('plugin-meetings', () => {
3060
3095
 
3061
3096
  sandbox.stub(locusParser, 'isNewFullLocus').returns(false);
3062
3097
 
3063
- locusInfo.onFullLocus(fakeLocus, eventType);
3098
+ locusInfo.onFullLocus('test', fakeLocus, eventType);
3064
3099
 
3065
3100
  spies.forEach((spy) => {
3066
3101
  assert.notCalled(spy);
@@ -3210,7 +3245,11 @@ describe('plugin-meetings', () => {
3210
3245
  }).then(() => {
3211
3246
  assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldLocusUrl'});
3212
3247
 
3213
- assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
3248
+ assert.calledOnceWithExactly(
3249
+ meeting.locusInfo.onFullLocus,
3250
+ 'classic Locus sync',
3251
+ fakeFullLocusDto
3252
+ );
3214
3253
  assert.calledOnce(locusInfo.locusParser.resume);
3215
3254
  });
3216
3255
  });
@@ -3308,7 +3347,11 @@ describe('plugin-meetings', () => {
3308
3347
  });
3309
3348
 
3310
3349
  assert.notCalled(meeting.locusInfo.handleLocusDelta);
3311
- assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
3350
+ assert.calledOnceWithExactly(
3351
+ meeting.locusInfo.onFullLocus,
3352
+ 'classic Locus sync',
3353
+ fakeFullLocusDto
3354
+ );
3312
3355
  assert.calledOnce(locusInfo.locusParser.resume);
3313
3356
  });
3314
3357
  });
@@ -3484,7 +3527,11 @@ describe('plugin-meetings', () => {
3484
3527
  url: 'fake locus DELTA url',
3485
3528
  });
3486
3529
  assert.notCalled(meeting.locusInfo.handleLocusDelta);
3487
- assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
3530
+ assert.calledOnceWithExactly(
3531
+ meeting.locusInfo.onFullLocus,
3532
+ 'classic Locus sync',
3533
+ fakeFullLocusDto
3534
+ );
3488
3535
  assert.calledOnce(locusInfo.locusParser.resume);
3489
3536
  });
3490
3537
  });
@@ -3950,7 +3997,7 @@ describe('plugin-meetings', () => {
3950
3997
  getLocusDTO: syncRequestStub,
3951
3998
  };
3952
3999
 
3953
- locusInfo.onFullLocus({
4000
+ locusInfo.onFullLocus('test', {
3954
4001
  sequence: {
3955
4002
  rangeStart: 0,
3956
4003
  rangeEnd: 0,
@@ -4213,6 +4260,30 @@ describe('plugin-meetings', () => {
4213
4260
 
4214
4261
  assert.calledOnceWithExactly(mockHashTreeParser.handleMessage, fakeHashTreeMessage);
4215
4262
  });
4263
+
4264
+ it('ignores hash tree event when hashTreeParser is not created yet', () => {
4265
+ const data = {
4266
+ eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
4267
+ stateElementsMessage: {
4268
+ locusStateElements: [],
4269
+ dataSets: [],
4270
+ },
4271
+ };
4272
+
4273
+ const loggerSpy = sinon.spy(LoggerProxy.logger, 'info');
4274
+ const getTheLocusToUpdateStub = sinon.stub(locusInfo, 'getTheLocusToUpdate');
4275
+
4276
+ // Ensure we're not using hash trees
4277
+ assert.isUndefined(locusInfo.hashTreeParser);
4278
+
4279
+ locusInfo.parse(mockMeeting, data);
4280
+
4281
+ assert.calledWith(
4282
+ loggerSpy,
4283
+ 'Locus-info:index#parse --> received locus hash tree event before hashTreeParser is created'
4284
+ );
4285
+ assert.notCalled(getTheLocusToUpdateStub);
4286
+ });
4216
4287
  });
4217
4288
  });
4218
4289
  });
@@ -5,6 +5,8 @@ import {ConnectionState, MediaConnectionEventNames} from '@webex/internal-media-
5
5
  import testUtils from '../../../utils/testUtils';
6
6
  import {ICE_AND_DTLS_CONNECTION_TIMEOUT} from '@webex/plugin-meetings/src/constants';
7
7
  import MediaConnectionAwaiter from '../../../../src/media/MediaConnectionAwaiter';
8
+ import Metrics from '../../../../src/metrics';
9
+ import BEHAVIORAL_METRICS from '../../../../src/metrics/constants';
8
10
 
9
11
  describe('MediaConnectionAwaiter', () => {
10
12
  let mediaConnectionAwaiter;
@@ -14,18 +16,34 @@ describe('MediaConnectionAwaiter', () => {
14
16
  beforeEach(() => {
15
17
  clock = sinon.useFakeTimers();
16
18
 
19
+ const mockTransportReport = {
20
+ type: 'transport',
21
+ dtlsState: 'connecting',
22
+ iceState: 'checking',
23
+ packetsSent: 10,
24
+ packetsReceived: 5,
25
+ };
26
+
17
27
  mockMC = {
18
- getStats: sinon.stub().resolves([]),
28
+ getStats: sinon.stub().resolves({
29
+ values: () => [mockTransportReport],
30
+ }),
19
31
  on: sinon.stub(),
20
32
  off: sinon.stub(),
21
33
  getConnectionState: sinon.stub().returns(ConnectionState.New),
22
34
  getIceGatheringState: sinon.stub().returns('new'),
23
35
  getIceConnectionState: sinon.stub().returns('new'),
24
36
  getPeerConnectionState: sinon.stub().returns('new'),
37
+ multistreamConnection: {
38
+ dataChannel: {
39
+ readyState: 'open',
40
+ },
41
+ },
25
42
  };
26
43
 
27
44
  mediaConnectionAwaiter = new MediaConnectionAwaiter({
28
45
  webrtcMediaConnection: mockMC,
46
+ correlationId: 'test-correlation-id',
29
47
  });
30
48
  });
31
49
 
@@ -44,6 +62,8 @@ describe('MediaConnectionAwaiter', () => {
44
62
  });
45
63
 
46
64
  it('rejects after timeout if ice state is not connected', async () => {
65
+ const sendMetricSpy = sinon.spy(Metrics, 'sendBehavioralMetric');
66
+
47
67
  mockMC.getConnectionState.returns(ConnectionState.Connecting);
48
68
  mockMC.getIceGatheringState.returns('gathering');
49
69
 
@@ -83,6 +103,18 @@ describe('MediaConnectionAwaiter', () => {
83
103
  assert.equal(promiseRejected, true);
84
104
 
85
105
  assert.calledThrice(mockMC.off);
106
+
107
+ assert.calledOnceWithExactly(sendMetricSpy, BEHAVIORAL_METRICS.MEDIA_STILL_NOT_CONNECTED, {
108
+ correlation_id: 'test-correlation-id',
109
+ numTransports: 1,
110
+ dtlsState: 'connecting',
111
+ iceState: 'checking',
112
+ packetsSent: 10,
113
+ packetsReceived: 5,
114
+ dataChannelState: 'open',
115
+ });
116
+
117
+ sendMetricSpy.restore();
86
118
  });
87
119
 
88
120
  it('rejects immediately if ice state is FAILED', async () => {
@@ -351,6 +383,8 @@ describe('MediaConnectionAwaiter', () => {
351
383
  });
352
384
 
353
385
  it(`reject with restart timer once if gathering state is not complete`, async () => {
386
+ const sendMetricSpy = sinon.spy(Metrics, 'sendBehavioralMetric');
387
+
354
388
  mockMC.getConnectionState.returns(ConnectionState.Connecting);
355
389
  mockMC.getIceGatheringState.returns('new');
356
390
 
@@ -390,6 +424,12 @@ describe('MediaConnectionAwaiter', () => {
390
424
 
391
425
  assert.calledOnce(clearTimeoutSpy);
392
426
  assert.calledTwice(setTimeoutSpy);
427
+
428
+ // verify sendMetric was called twice (once for each timeout)
429
+ assert.calledTwice(sendMetricSpy);
430
+ assert.calledWith(sendMetricSpy, BEHAVIORAL_METRICS.MEDIA_STILL_NOT_CONNECTED);
431
+
432
+ sendMetricSpy.restore();
393
433
  });
394
434
 
395
435
  it(`resolves gathering and connection state complete right after`, async () => {
@@ -41,14 +41,23 @@ describe('MediaProperties', () => {
41
41
  describe('waitForMediaConnectionConnected', () => {
42
42
  it('resolves if media connection is connected', async () => {
43
43
  const waitForMediaConnectionConnectedResult = new Defer();
44
+ const correlationId = 'aaaa-bbbb-cccc-dddd';
44
45
 
45
- sinon
46
+ let capturedInstance;
47
+ const stub = sinon
46
48
  .stub(MediaConnectionAwaiter.prototype, 'waitForMediaConnectionConnected')
47
- .returns(waitForMediaConnectionConnectedResult.promise);
49
+ .callsFake(function () {
50
+ capturedInstance = this;
51
+ return waitForMediaConnectionConnectedResult.promise;
52
+ });
48
53
 
49
54
  waitForMediaConnectionConnectedResult.resolve();
50
55
 
51
- await mediaProperties.waitForMediaConnectionConnected();
56
+ await mediaProperties.waitForMediaConnectionConnected(correlationId);
57
+
58
+ assert.calledOnce(stub);
59
+ assert.equal(capturedInstance.correlationId, correlationId);
60
+ assert.equal(capturedInstance.webrtcMediaConnection, mockMC);
52
61
  });
53
62
  it('rejects if media connection is not connected', async () => {
54
63
  const waitForMediaConnectionConnectedResult = new Defer();
@@ -1249,7 +1249,7 @@ describe('plugin-meetings', () => {
1249
1249
  });
1250
1250
 
1251
1251
  [
1252
- {errorName: 'SdpOfferCreationError', description: 'if we fail to create the offer on first attempt'},
1252
+ {errorName: 'SdpOfferCreationError', description: 'if we fail to create the offer on first attempt'},
1253
1253
  {errorName: 'WebrtcApiNotAvailableError', description: 'if RTCPeerConnection is not available'},
1254
1254
  ].forEach(({errorName, description}) => {
1255
1255
  it(`should not attempt a retry ${description}`, async () => {
@@ -1882,6 +1882,53 @@ describe('plugin-meetings', () => {
1882
1882
  fakeProcessedReaction
1883
1883
  );
1884
1884
  });
1885
+
1886
+ it('should process if participantId does not exist in membersCollection but has displayName in Webinar', () => {
1887
+ LoggerProxy.logger.warn = sinon.stub();
1888
+ meeting.isReactionsSupported = sinon.stub().returns(true);
1889
+ meeting.config.receiveReactions = true;
1890
+ meeting.locusInfo.info = {isWebinar: true};
1891
+ const fakeSendersName = 'Fake reactors name';
1892
+ const fakeReactionPayload = {
1893
+ type: 'fake_type',
1894
+ codepoints: 'fake_codepoints',
1895
+ shortcodes: 'fake_shortcodes',
1896
+ tone: {
1897
+ type: 'fake_tone_type',
1898
+ codepoints: 'fake_tone_codepoints',
1899
+ shortcodes: 'fake_tone_shortcodes',
1900
+ },
1901
+ };
1902
+ const fakeSenderPayload = {
1903
+ displayName: 'Fake reactors name',
1904
+ participantId: 'fake_participant_id',
1905
+ };
1906
+ const fakeProcessedReaction = {
1907
+ reaction: fakeReactionPayload,
1908
+ sender: {
1909
+ id: fakeSenderPayload.participantId,
1910
+ name: fakeSendersName,
1911
+ },
1912
+ };
1913
+ const fakeRelayEvent = {
1914
+ data: {
1915
+ relayType: REACTION_RELAY_TYPES.REACTION,
1916
+ reaction: fakeReactionPayload,
1917
+ sender: fakeSenderPayload,
1918
+ },
1919
+ };
1920
+ meeting.processRelayEvent(fakeRelayEvent);
1921
+ assert.calledWith(
1922
+ TriggerProxy.trigger,
1923
+ sinon.match.instanceOf(Meeting),
1924
+ {
1925
+ file: 'meeting/index',
1926
+ function: 'join',
1927
+ },
1928
+ EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
1929
+ fakeProcessedReaction
1930
+ );
1931
+ });
1885
1932
  });
1886
1933
 
1887
1934
  describe('#handleLLMOnline', () => {
@@ -3028,6 +3075,111 @@ describe('plugin-meetings', () => {
3028
3075
  checkWorking({allowMediaInLobby: true});
3029
3076
  });
3030
3077
 
3078
+ const setupLobbyTest = () => {
3079
+ meeting.roap.doTurnDiscovery = sinon
3080
+ .stub()
3081
+ .resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
3082
+
3083
+ meeting.meetingState = 'ACTIVE';
3084
+ meeting.locusInfo.parsedLocus = {self: {state: 'IDLE'}};
3085
+ meeting.isUserUnadmitted = true;
3086
+
3087
+ // Mock locusMediaRequest
3088
+ meeting.locusMediaRequest = {
3089
+ send: sinon.stub().resolves(),
3090
+ isConfluenceCreated: sinon.stub().returns(false),
3091
+ };
3092
+
3093
+ sinon.stub(RemoteMediaManagerModule, 'RemoteMediaManager').returns({
3094
+ start: sinon.stub().resolves(),
3095
+ on: sinon.stub(),
3096
+ logAllReceiveSlots: sinon.stub(),
3097
+ });
3098
+
3099
+ meeting.isMultistream = true;
3100
+
3101
+ const createFakeStream = (id) => ({
3102
+ on: sinon.stub(),
3103
+ off: sinon.stub(),
3104
+ userMuted: false,
3105
+ systemMuted: false,
3106
+ get muted() {
3107
+ return this.userMuted || this.systemMuted;
3108
+ },
3109
+ setUnmuteAllowed: sinon.stub(),
3110
+ setUserMuted: sinon.stub(),
3111
+ outputStream: {
3112
+ getTracks: () => [{id}],
3113
+ },
3114
+ getSettings: sinon.stub().returns({}),
3115
+ });
3116
+
3117
+ return {
3118
+ fakeMicrophoneStream: createFakeStream('fake mic'),
3119
+ fakeCameraStream: createFakeStream('fake camera'),
3120
+ };
3121
+ };
3122
+
3123
+ it('should not publish any local streams when in the lobby and allowPublishMediaInLobby is false', async () => {
3124
+ const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
3125
+
3126
+ const publishStreamStub = sinon.stub();
3127
+ fakeMediaConnection.createSendSlot = sinon.stub().returns({
3128
+ publishStream: publishStreamStub,
3129
+ unpublishStream: sinon.stub(),
3130
+ setNamedMediaGroups: sinon.stub(),
3131
+ });
3132
+
3133
+ await meeting.addMedia({
3134
+ allowMediaInLobby: true,
3135
+ allowPublishMediaInLobby: false,
3136
+ audioEnabled: true,
3137
+ videoEnabled: true,
3138
+ localStreams: {
3139
+ microphone: fakeMicrophoneStream,
3140
+ camera: fakeCameraStream,
3141
+ },
3142
+ });
3143
+
3144
+ assert.notCalled(publishStreamStub);
3145
+ });
3146
+
3147
+ it('should publish local streams when in the lobby and allowPublishMediaInLobby is true', async () => {
3148
+ const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
3149
+
3150
+ const audioSlot = {
3151
+ publishStream: sinon.stub(),
3152
+ unpublishStream: sinon.stub(),
3153
+ setNamedMediaGroups: sinon.stub(),
3154
+ };
3155
+ const videoSlot = {
3156
+ publishStream: sinon.stub(),
3157
+ unpublishStream: sinon.stub(),
3158
+ setNamedMediaGroups: sinon.stub(),
3159
+ };
3160
+
3161
+ fakeMediaConnection.createSendSlot = sinon.stub().callsFake((mediaType) => {
3162
+ if (mediaType === 'AUDIO-MAIN') {
3163
+ return audioSlot;
3164
+ }
3165
+ return videoSlot;
3166
+ });
3167
+
3168
+ await meeting.addMedia({
3169
+ allowMediaInLobby: true,
3170
+ allowPublishMediaInLobby: true,
3171
+ audioEnabled: true,
3172
+ videoEnabled: true,
3173
+ localStreams: {
3174
+ microphone: fakeMicrophoneStream,
3175
+ camera: fakeCameraStream,
3176
+ },
3177
+ });
3178
+
3179
+ assert.calledOnceWithExactly(audioSlot.publishStream, fakeMicrophoneStream);
3180
+ assert.calledOnceWithExactly(videoSlot.publishStream, fakeCameraStream);
3181
+ });
3182
+
3031
3183
  it('should create rtcMetrics and pass them to Media.createMediaConnection()', async () => {
3032
3184
  const setIntervalOriginal = window.setInterval;
3033
3185
  window.setInterval = sinon.stub().returns(1);
@@ -9149,7 +9301,10 @@ describe('plugin-meetings', () => {
9149
9301
 
9150
9302
  // check that the right things were called by the callback
9151
9303
  assert.calledOnceWithExactly(meeting.waitForRemoteSDPAnswer);
9152
- assert.calledOnceWithExactly(meeting.mediaProperties.waitForMediaConnectionConnected);
9304
+ assert.calledOnceWithExactly(
9305
+ meeting.mediaProperties.waitForMediaConnectionConnected,
9306
+ meeting.correlationId
9307
+ );
9153
9308
  });
9154
9309
  });
9155
9310
 
@@ -12628,6 +12783,7 @@ describe('plugin-meetings', () => {
12628
12783
 
12629
12784
  it('should read the locus object, set on the meeting and return null', () => {
12630
12785
  const dataSets = {someFakeStuff: 'dataSet'};
12786
+ const metadata = {some: 'metadata'};
12631
12787
 
12632
12788
  meeting.setLocus({
12633
12789
  mediaConnections: [test1],
@@ -12637,12 +12793,14 @@ describe('plugin-meetings', () => {
12637
12793
  mediaId: uuid3,
12638
12794
  locus: {host: {id: uuid4}},
12639
12795
  dataSets,
12796
+ metadata,
12640
12797
  });
12641
12798
  assert.calledOnce(meeting.locusInfo.initialSetup);
12642
12799
  assert.calledWith(meeting.locusInfo.initialSetup, {
12643
12800
  trigger: 'join-response',
12644
12801
  locus: {host: {id: uuid4}},
12645
12802
  dataSets,
12803
+ metadata,
12646
12804
  });
12647
12805
  assert.equal(meeting.mediaConnections, test1);
12648
12806
  assert.equal(meeting.locusUrl, url1);