@webex/plugin-meetings 3.0.0-beta.35 → 3.0.0-beta.36

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 (52) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/edit-lock-error.js +52 -0
  3. package/dist/breakouts/edit-lock-error.js.map +1 -0
  4. package/dist/breakouts/index.js +86 -2
  5. package/dist/breakouts/index.js.map +1 -1
  6. package/dist/constants.js +12 -1
  7. package/dist/constants.js.map +1 -1
  8. package/dist/media/index.js +4 -18
  9. package/dist/media/index.js.map +1 -1
  10. package/dist/media/properties.js +3 -3
  11. package/dist/media/properties.js.map +1 -1
  12. package/dist/meeting/index.js +194 -306
  13. package/dist/meeting/index.js.map +1 -1
  14. package/dist/meeting/muteState.js +7 -2
  15. package/dist/meeting/muteState.js.map +1 -1
  16. package/dist/meeting/util.js +2 -2
  17. package/dist/meeting/util.js.map +1 -1
  18. package/dist/metrics/constants.js +0 -4
  19. package/dist/metrics/constants.js.map +1 -1
  20. package/dist/reconnection-manager/index.js +1 -2
  21. package/dist/reconnection-manager/index.js.map +1 -1
  22. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  23. package/dist/types/constants.d.ts +11 -0
  24. package/dist/types/media/properties.d.ts +7 -6
  25. package/dist/types/meeting/index.d.ts +11 -36
  26. package/dist/types/metrics/constants.d.ts +0 -4
  27. package/package.json +19 -19
  28. package/src/breakouts/README.md +8 -2
  29. package/src/breakouts/edit-lock-error.ts +25 -0
  30. package/src/breakouts/index.ts +73 -0
  31. package/src/constants.ts +11 -0
  32. package/src/media/index.ts +14 -24
  33. package/src/media/properties.ts +16 -10
  34. package/src/meeting/index.ts +122 -204
  35. package/src/meeting/muteState.ts +5 -5
  36. package/src/meeting/util.ts +5 -4
  37. package/src/metrics/constants.ts +0 -4
  38. package/src/reconnection-manager/index.ts +1 -1
  39. package/test/integration/spec/converged-space-meetings.js +3 -3
  40. package/test/integration/spec/journey.js +3 -3
  41. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  42. package/test/unit/spec/breakouts/index.ts +92 -1
  43. package/test/unit/spec/media/index.ts +8 -6
  44. package/test/unit/spec/meeting/index.js +87 -114
  45. package/test/unit/spec/meeting/muteState.js +21 -22
  46. package/test/unit/spec/meeting/utils.js +3 -1
  47. package/test/utils/testUtils.js +30 -25
  48. package/dist/meeting/effectsState.js +0 -262
  49. package/dist/meeting/effectsState.js.map +0 -1
  50. package/dist/types/meeting/effectsState.d.ts +0 -42
  51. package/src/meeting/effectsState.ts +0 -211
  52. package/test/unit/spec/meeting/effectsState.js +0 -285
@@ -1,4 +1,5 @@
1
1
  import {isEmpty} from 'lodash';
2
+ import {LocalCameraTrack, LocalMicrophoneTrack} from '@webex/internal-media-core';
2
3
 
3
4
  import {MeetingNotActiveError, UserNotJoinedError} from '../common/errors/webex-errors';
4
5
  import Metrics from '../metrics';
@@ -390,11 +391,11 @@ MeetingUtil.unlockMeeting = (actions, request, locusUrl) => {
390
391
  return Promise.reject(new PermissionError('Unlock not allowed, due to joined property.'));
391
392
  };
392
393
 
393
- MeetingUtil.handleAudioLogging = (audioTrack) => {
394
+ MeetingUtil.handleAudioLogging = (audioTrack: LocalMicrophoneTrack | null) => {
394
395
  const LOG_HEADER = 'MeetingUtil#handleAudioLogging -->';
395
396
 
396
397
  if (audioTrack) {
397
- const settings = audioTrack.getSettings();
398
+ const settings = audioTrack.underlyingTrack.getSettings();
398
399
  const {deviceId} = settings;
399
400
 
400
401
  LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${deviceId}`);
@@ -402,11 +403,11 @@ MeetingUtil.handleAudioLogging = (audioTrack) => {
402
403
  }
403
404
  };
404
405
 
405
- MeetingUtil.handleVideoLogging = (videoTrack) => {
406
+ MeetingUtil.handleVideoLogging = (videoTrack: LocalCameraTrack | null) => {
406
407
  const LOG_HEADER = 'MeetingUtil#handleVideoLogging -->';
407
408
 
408
409
  if (videoTrack) {
409
- const settings = videoTrack.getSettings();
410
+ const settings = videoTrack.underlyingTrack.getSettings();
410
411
  const {deviceId} = settings;
411
412
 
412
413
  LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${deviceId}`);
@@ -40,10 +40,6 @@ const BEHAVIORAL_METRICS = {
40
40
  INVALID_ICE_CANDIDATE: 'js_sdk_invalid_ice_candidate',
41
41
  UPLOAD_LOGS_FAILURE: 'js_sdk_upload_logs_failure',
42
42
  RECEIVE_TRANSCRIPTION_FAILURE: 'js_sdk_receive_transcription_failure',
43
- ENABLE_BNR_SUCCESS: 'js_sdk_enable_bnr_success',
44
- ENABLE_BNR_FAILURE: 'js_sdk_enable_bnr_failure',
45
- DISABLE_BNR_SUCCESS: 'js_sdk_disable_bnr_success',
46
- DISABLE_BNR_FAILURE: 'js_sdk_disable_bnr_failure',
47
43
  FETCH_MEETING_INFO_V1_SUCCESS: 'js_sdk_fetch_meeting_info_v1_success',
48
44
  FETCH_MEETING_INFO_V1_FAILURE: 'js_sdk_fetch_meeting_info_v1_failure',
49
45
  ADHOC_MEETING_SUCCESS: 'js_sdk_adhoc_meeting_success',
@@ -476,7 +476,7 @@ export default class ReconnectionManager {
476
476
 
477
477
  if (wasSharing) {
478
478
  // Stop the share streams if user tried to rejoin
479
- Media.stopTracks(this.meeting.mediaProperties.shareTrack);
479
+ this.meeting.setLocalShareTrack(null);
480
480
  this.meeting.isSharing = false;
481
481
  if (this.shareStatus === SHARE_STATUS.LOCAL_SHARE_ACTIVE) {
482
482
  this.meeting.shareStatus = SHARE_STATUS.NO_SHARE;
@@ -142,9 +142,9 @@ skipInNode(describe)('plugin-meetings', () => {
142
142
  {scope: chris.meeting, event: 'media:negotiated'},
143
143
  ]);
144
144
 
145
- const addMediaAlice = testUtils.addMedia(alice, {expectedMediaReadyTypes: ['local']});
146
- const addMediaBob = testUtils.addMedia(bob, {expectedMediaReadyTypes: ['local']});
147
- const addMediaChris = testUtils.addMedia(chris, {expectedMediaReadyTypes: ['local']});
145
+ const addMediaAlice = testUtils.addMedia(alice, {multistream: true, expectedMediaReadyTypes: ['local']});
146
+ const addMediaBob = testUtils.addMedia(bob, {multistream: true, expectedMediaReadyTypes: ['local']});
147
+ const addMediaChris = testUtils.addMedia(chris, {multistream: true, expectedMediaReadyTypes: ['local']});
148
148
 
149
149
  await addMediaAlice;
150
150
  await addMediaBob;
@@ -605,7 +605,7 @@ skipInNode(describe)('plugin-meetings', () => {
605
605
  ]).then(() => {
606
606
  // TODO: Re-eanable Safari when screensharing issues have been resolved
607
607
  if (!isBrowser('safari')) {
608
- assert.equal(alice.meeting.mediaProperties.shareTrack.getConstraints().height, 720);
608
+ assert.equal(alice.meeting.mediaProperties.shareTrack.underlyingTrack.getConstraints().height, 720);
609
609
  }
610
610
  assert.equal(alice.meeting.isSharing, true);
611
611
  assert.equal(alice.meeting.shareStatus, 'local_share_active');
@@ -646,7 +646,7 @@ skipInNode(describe)('plugin-meetings', () => {
646
646
  // TODO: Re-eanable Safari when screensharing issues have been resolved
647
647
  if (!isBrowser('safari')) {
648
648
  assert.equal(
649
- bob.meeting.mediaProperties.shareTrack.getConstraints().height,
649
+ bob.meeting.mediaProperties.shareTrack.underlyingTrack.getConstraints().height,
650
650
  heightResolution
651
651
  );
652
652
  }
@@ -806,7 +806,7 @@ skipInNode(describe)('plugin-meetings', () => {
806
806
  // TODO: Re-eanable Safari when screensharing issues have been resolved
807
807
  if (!isBrowser('safari')) {
808
808
  assert.equal(
809
- bob.meeting.mediaProperties.shareTrack.getConstraints().height,
809
+ bob.meeting.mediaProperties.shareTrack.underlyingTrack.getConstraints().height,
810
810
  heightResolution
811
811
  );
812
812
  }
@@ -0,0 +1,30 @@
1
+ import BreakoutEditLockedError from '@webex/plugin-meetings/src/breakouts/edit-lock-error';
2
+ import {assert, expect} from '@webex/test-helper-chai';
3
+ import {ERROR_DICTIONARY} from '@webex/plugin-meetings/src/constants';
4
+ import {BREAKOUTS} from '@webex/plugin-meetings/src/constants';
5
+
6
+ describe('plugin-meetings', () => {
7
+ describe('edit-lock-error', () => {
8
+ it('check constructor paramter', () => {
9
+ const result = new BreakoutEditLockedError();
10
+ assert.equal(result.name, ERROR_DICTIONARY.BREAKOUT_EDIT.NAME);
11
+ assert.equal(result.sdkMessage, ERROR_DICTIONARY.BREAKOUT_EDIT.MESSAGE);
12
+ assert.equal(result.code, ERROR_DICTIONARY.BREAKOUT_EDIT.CODE);
13
+ assert.equal(result.error, null);
14
+ });
15
+
16
+ it('check constructor paramter', () => {
17
+ const fakeError = {
18
+ body: {
19
+ "errorCode":BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
20
+ "message":"Edit lock token mismatch"
21
+ }
22
+ };
23
+ const result = new BreakoutEditLockedError('message', fakeError);
24
+ assert.equal(result.name, ERROR_DICTIONARY.BREAKOUT_EDIT.NAME);
25
+ assert.equal(result.sdkMessage, ERROR_DICTIONARY.BREAKOUT_EDIT.MESSAGE);
26
+ assert.equal(result.code, ERROR_DICTIONARY.BREAKOUT_EDIT.CODE);
27
+ assert.equal(result.error, fakeError);
28
+ });
29
+ });
30
+ });
@@ -7,7 +7,7 @@ import {BREAKOUTS} from '@webex/plugin-meetings/src/constants';
7
7
  import sinon from "sinon";
8
8
  import MockWebex from '@webex/test-helper-mock-webex';
9
9
  import testUtils from '../../../utils/testUtils';
10
-
10
+ import BreakoutEditLockedError from "@webex/plugin-meetings/src/breakouts/edit-lock-error";
11
11
 
12
12
  describe('plugin-meetings', () => {
13
13
  describe('Breakouts', () => {
@@ -460,5 +460,96 @@ describe('plugin-meetings', () => {
460
460
  assert.equal(result, 'REQUEST_RETURN_VALUE');
461
461
  });
462
462
  });
463
+
464
+ describe('delete', () => {
465
+ it('makes the request as expected', async () => {
466
+ webex.request.returns(Promise.resolve({
467
+ body: {
468
+ groups: [{
469
+ id : "455556a4-37cd-4baa-89bc-8730581a1cc0",
470
+ status : "CLOSE",
471
+ type : "BREAKOUT",
472
+ }]
473
+ }
474
+ }));
475
+
476
+ const result = await breakouts.clearSessions();
477
+ assert.calledOnceWithExactly(webex.request, {
478
+ method: 'PUT',
479
+ uri: 'url',
480
+ body: {
481
+ groups: [
482
+ {
483
+ action: BREAKOUTS.ACTION_TYPES.DELETE,
484
+ },
485
+ ],
486
+ }
487
+ });
488
+
489
+ assert.equal(breakouts.groups[0].status, "CLOSE")
490
+ });
491
+
492
+ it('rejects when edit lock token mismatch', async () => {
493
+ webex.request.returns(Promise.reject({
494
+ body: {
495
+ "errorCode":BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
496
+ "message":"Edit lock token mismatch"
497
+ }
498
+ }));
499
+
500
+ await assert.isRejected(
501
+ breakouts.clearSessions(),
502
+ BreakoutEditLockedError,
503
+ 'Edit lock token mismatch'
504
+ );
505
+
506
+ });
507
+ });
508
+
509
+ describe('create', () => {
510
+ it('response not include groups info', async () => {
511
+ const sessions = [{'name':'session1', "anyoneCanJoin" : true}];
512
+ const result = await breakouts.create(sessions);
513
+
514
+ assert.equal(result, 'REQUEST_RETURN_VALUE');
515
+
516
+ });
517
+
518
+ it('response include groups info', async () => {
519
+ const sessions = [{'name':'session1', "anyoneCanJoin" : true}];
520
+
521
+ webex.request.returns(Promise.resolve({
522
+ body: {
523
+ groups: [{
524
+ id : "455556a4-37cd-4baa-89bc-8730581a1cc0",
525
+ status : "PENDING",
526
+ type : "BREAKOUT",
527
+ }]
528
+ }
529
+ }));
530
+
531
+ const result = await breakouts.create(sessions);
532
+
533
+ assert.equal(breakouts.groups[0].id, "455556a4-37cd-4baa-89bc-8730581a1cc0")
534
+
535
+ });
536
+
537
+ it('rejects when edit lock token mismatch', async () => {
538
+ const sessions = [{'name':'session1', "anyoneCanJoin" : true}];
539
+
540
+ webex.request.returns(Promise.reject({
541
+ body: {
542
+ "errorCode":BREAKOUTS.ERROR_CODE.EDIT_LOCK_TOKEN_MISMATCH,
543
+ "message":"Edit lock token mismatch"
544
+ }
545
+ }));
546
+
547
+ await assert.isRejected(
548
+ breakouts.create(sessions),
549
+ BreakoutEditLockedError,
550
+ 'Edit lock token mismatch'
551
+ );
552
+ });
553
+ });
463
554
  });
464
555
  });
@@ -10,9 +10,11 @@ describe('createMediaConnection', () => {
10
10
  };
11
11
  const fakeAudioTrack = {
12
12
  id: 'audio track',
13
+ underlyingTrack: 'underlying audio track',
13
14
  };
14
15
  const fakeVideoTrack = {
15
16
  id: 'video track',
17
+ underlyingTrack: 'underlying video track',
16
18
  };
17
19
 
18
20
  afterEach(() => {
@@ -80,9 +82,9 @@ describe('createMediaConnection', () => {
80
82
  },
81
83
  {
82
84
  send: {
83
- audio: fakeAudioTrack,
84
- video: fakeVideoTrack,
85
- screenShareVideo: null,
85
+ audio: fakeAudioTrack.underlyingTrack,
86
+ video: fakeVideoTrack.underlyingTrack,
87
+ screenShareVideo: undefined,
86
88
  },
87
89
  receive: {
88
90
  audio: true,
@@ -190,9 +192,9 @@ describe('createMediaConnection', () => {
190
192
  },
191
193
  {
192
194
  send: {
193
- audio: fakeAudioTrack,
194
- video: null,
195
- screenShareVideo: fakeVideoTrack,
195
+ audio: fakeAudioTrack.underlyingTrack,
196
+ video: undefined,
197
+ screenShareVideo: fakeVideoTrack.underlyingTrack,
196
198
  },
197
199
  receive: {
198
200
  audio: true,
@@ -22,7 +22,8 @@ import {
22
22
  LOCUSINFO,
23
23
  PC_BAIL_TIMEOUT,
24
24
  } from '@webex/plugin-meetings/src/constants';
25
- import {ConnectionState, Event, Errors, ErrorType, RemoteTrackType} from '@webex/internal-media-core';
25
+ import * as InternalMediaCoreModule from '@webex/internal-media-core';
26
+ import {ConnectionState, Event, Errors, ErrorType, LocalTrackEvents, RemoteTrackType} from '@webex/internal-media-core';
26
27
  import * as StatsAnalyzerModule from '@webex/plugin-meetings/src/statsAnalyzer';
27
28
  import * as MuteStateModule from '@webex/plugin-meetings/src/meeting/muteState';
28
29
  import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
@@ -194,7 +195,11 @@ describe('plugin-meetings', () => {
194
195
  TriggerProxy.trigger = sinon.stub().returns(true);
195
196
  Metrics.postEvent = sinon.stub();
196
197
  Metrics.initialSetup(null, webex);
197
- MediaUtil.createMediaStream = sinon.stub().returns(true);
198
+ MediaUtil.createMediaStream = sinon.stub().callsFake((tracks) => {
199
+ return {
200
+ getTracks: () => tracks
201
+ };
202
+ });;
198
203
 
199
204
  uuid1 = uuid.v4();
200
205
  uuid2 = uuid.v4();
@@ -483,85 +488,6 @@ describe('plugin-meetings', () => {
483
488
  });
484
489
  });
485
490
  });
486
- describe('#enableBNR', () => {
487
- it('should have #enableBnr', () => {
488
- assert.exists(meeting.enableBNR);
489
- });
490
-
491
- describe('before audio attached to meeting', () => {
492
- it('should throw no audio error', async () => {
493
- await meeting.enableBNR().catch((err) => {
494
- assert.equal(err.toString(), "Error: Meeting doesn't have an audioTrack attached");
495
- });
496
- });
497
- });
498
-
499
- describe('after audio attached to meeting', () => {
500
- let handleClientRequest;
501
-
502
- beforeEach(async () => {
503
- await meeting.getMediaStreams();
504
- await meeting.addMedia();
505
- });
506
-
507
- it('should throw error if meeting audio is muted', async () => {
508
- const handleClientRequest = (meeting, mute) => {
509
- meeting.mediaProperties.audioTrack.enabled = !mute;
510
-
511
- return Promise.resolve();
512
- };
513
- const isMuted = () => !meeting.mediaProperties.audioTrack.enabled;
514
-
515
- meeting.locusInfo.parsedLocus = {self: {state: 'JOINED'}};
516
- meeting.mediaId = 'mediaId';
517
- meeting.audio = {handleClientRequest, isMuted};
518
- await meeting.muteAudio();
519
- await meeting.enableBNR().catch((err) => {
520
- assert.equal(err.message, 'Cannot enable BNR while meeting is muted');
521
- });
522
- });
523
-
524
- it('should return true on enable bnr success', async () => {
525
- handleClientRequest = sinon.stub().returns(Promise.resolve(true));
526
- meeting.effects = {handleClientRequest};
527
- const response = await meeting.enableBNR();
528
-
529
- assert.equal(response, true);
530
- });
531
- });
532
- });
533
-
534
- describe('#disableBNR', () => {
535
- describe('before audio attached to meeting', () => {
536
- it('should have #disableBnr', () => {
537
- assert.exists(meeting.disableBNR);
538
- });
539
-
540
- it('should throw no audio error', async () => {
541
- await meeting.disableBNR().catch((err) => {
542
- assert.equal(err.toString(), "Error: Meeting doesn't have an audioTrack attached");
543
- });
544
- });
545
- });
546
- describe('after audio attached to meeting', () => {
547
- beforeEach(async () => {
548
- await meeting.getMediaStreams();
549
- await meeting.addMedia();
550
- });
551
-
552
- let handleClientRequest;
553
- let isBnrEnabled;
554
-
555
- it('should return true on disable bnr success', async () => {
556
- handleClientRequest = sinon.stub().returns(Promise.resolve(true));
557
- isBnrEnabled = sinon.stub().returns(Promise.resolve(true));
558
- meeting.effects = {handleClientRequest, isBnrEnabled};
559
- const response = await meeting.disableBNR();
560
-
561
- assert.equal(response, true);
562
- });
563
- });
564
- });
565
491
  });
566
492
  describe('#muteVideo', () => {
567
493
  it('should have #muteVideo', () => {
@@ -1311,8 +1237,7 @@ describe('plugin-meetings', () => {
1311
1237
  .addMedia({
1312
1238
  mediaSettings: {},
1313
1239
  })
1314
- .catch((error) => {
1315
- assert.equal(error.code, IceGatheringFailed.CODE);
1240
+ .catch(() => {
1316
1241
  errorThrown = true;
1317
1242
  });
1318
1243
 
@@ -1700,26 +1625,30 @@ describe('plugin-meetings', () => {
1700
1625
 
1701
1626
  it('skips canUpdateMedia() check on contentTracks.onended', () => {
1702
1627
  const {mediaProperties} = meeting;
1703
- let registeredListener = null;
1704
1628
  const fakeTrack = {
1705
1629
  getSettings: sinon.stub().returns({}),
1706
- onended: sinon.stub(),
1707
- addEventListener: sinon.stub().callsFake((event, listener) => {
1708
- registeredListener = listener;
1709
- }),
1710
1630
  };
1711
1631
 
1632
+ const listeners = {};
1633
+
1634
+ const fakeLocalDisplayTrack = {
1635
+ on: sinon.stub().callsFake((event, listener) => {
1636
+ listeners[event] = listener;
1637
+ })
1638
+ };
1639
+ sinon.stub(InternalMediaCoreModule, 'LocalDisplayTrack').returns(fakeLocalDisplayTrack);
1640
+
1641
+
1712
1642
  sandbox.stub(mediaProperties, 'setLocalShareTrack');
1713
- sandbox.stub(mediaProperties, 'shareTrack').value(fakeTrack);
1714
1643
  sandbox.stub(mediaProperties, 'setMediaSettings');
1715
1644
  sandbox.stub(meeting, 'stopShare').resolves(true);
1716
1645
  meeting.setLocalShareTrack(fakeTrack);
1717
1646
 
1718
- assert.calledOnce(fakeTrack.addEventListener);
1719
- assert.calledWith(fakeTrack.addEventListener, 'ended', sinon.match.any);
1720
- assert.isNotNull(registeredListener);
1647
+ assert.calledOnce(fakeLocalDisplayTrack.on);
1648
+ assert.calledWith(fakeLocalDisplayTrack.on, LocalTrackEvents.Ended, sinon.match.any);
1649
+ assert.isNotNull(listeners[LocalTrackEvents.Ended]);
1721
1650
 
1722
- registeredListener();
1651
+ listeners[LocalTrackEvents.Ended]();
1723
1652
 
1724
1653
  assert.calledWith(meeting.stopShare, {skipSignalingCheck: true});
1725
1654
  });
@@ -2135,7 +2064,7 @@ describe('plugin-meetings', () => {
2135
2064
  screenshareVideo: {
2136
2065
  id: 'fake share track',
2137
2066
  getSettings: sinon.stub().returns({}),
2138
- addEventListener: sinon.stub()
2067
+ on: sinon.stub()
2139
2068
  },
2140
2069
  };
2141
2070
 
@@ -2297,7 +2226,7 @@ describe('plugin-meetings', () => {
2297
2226
  screenshareVideo: {
2298
2227
  id: 'fake share track',
2299
2228
  getSettings: sinon.stub().returns({}),
2300
- addEventListener: sinon.stub(),
2229
+ on: sinon.stub(),
2301
2230
  },
2302
2231
  };
2303
2232
 
@@ -3582,6 +3511,12 @@ describe('plugin-meetings', () => {
3582
3511
  let videoTrack;
3583
3512
  let videoShareTrack;
3584
3513
  let createMuteStateStub;
3514
+ let LocalDisplayTrackConstructorStub;
3515
+ let LocalMicrophoneTrackConstructorStub;
3516
+ let LocalCameraTrackConstructorStub;
3517
+ let fakeLocalDisplayTrack;
3518
+ let fakeLocalMicrophoneTrack;
3519
+ let fakeLocalCameraTrack;
3585
3520
 
3586
3521
  beforeEach(() => {
3587
3522
  audioTrack = {
@@ -3594,7 +3529,7 @@ describe('plugin-meetings', () => {
3594
3529
  };
3595
3530
  videoShareTrack = {
3596
3531
  id: 'share track',
3597
- addEventListener: sinon.stub(),
3532
+ on: sinon.stub(),
3598
3533
  removeEventListener: sinon.stub(),
3599
3534
  getSettings: sinon.stub().returns({}),
3600
3535
  };
@@ -3606,6 +3541,28 @@ describe('plugin-meetings', () => {
3606
3541
  unpublishTrack: sinon.stub().resolves({}),
3607
3542
  };
3608
3543
 
3544
+ const createFakeLocalTrack = (originalTrack) => ({
3545
+ on: sinon.stub(),
3546
+ off: sinon.stub(),
3547
+ stop: sinon.stub(),
3548
+ originalTrack
3549
+ });
3550
+
3551
+ // setup mock constructors for webrtc-core local track classes in such a way
3552
+ // that they return the original track correctly (this is needed for unpublish() API tests)
3553
+ LocalDisplayTrackConstructorStub = sinon.stub(InternalMediaCoreModule, 'LocalDisplayTrack').callsFake((stream) => {
3554
+ fakeLocalDisplayTrack = createFakeLocalTrack(stream.getTracks()[0])
3555
+ return fakeLocalDisplayTrack;
3556
+ });
3557
+ LocalMicrophoneTrackConstructorStub = sinon.stub(InternalMediaCoreModule, 'LocalMicrophoneTrack').callsFake((stream) => {
3558
+ fakeLocalMicrophoneTrack = createFakeLocalTrack(stream.getTracks()[0])
3559
+ return fakeLocalMicrophoneTrack;
3560
+ });
3561
+ LocalCameraTrackConstructorStub = sinon.stub(InternalMediaCoreModule, 'LocalCameraTrack').callsFake((stream) => {
3562
+ fakeLocalCameraTrack = createFakeLocalTrack(stream.getTracks()[0])
3563
+ return fakeLocalCameraTrack;
3564
+ });
3565
+
3609
3566
  createMuteStateStub = sinon.stub(MuteStateModule, 'createMuteState').returns({id: 'fake mute state instance'});
3610
3567
  })
3611
3568
  describe('#publishTracks', () => {
@@ -3615,23 +3572,33 @@ describe('plugin-meetings', () => {
3615
3572
  });
3616
3573
 
3617
3574
  const checkAudioPublished = () => {
3575
+ assert.calledWith(MediaUtil.createMediaStream, [audioTrack]);
3576
+ assert.calledOnce(LocalMicrophoneTrackConstructorStub);
3577
+
3618
3578
  assert.calledWith(createMuteStateStub, 'audio', meeting, meeting.mediaProperties.mediaDirection);
3619
- assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.publishTrack, audioTrack, 'main');
3620
- assert.equal(meeting.mediaProperties.audioTrack, audioTrack);
3579
+ assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.publishTrack, fakeLocalMicrophoneTrack);
3580
+ assert.equal(meeting.mediaProperties.audioTrack, fakeLocalMicrophoneTrack);
3621
3581
  assert.equal(meeting.mediaProperties.mediaDirection.sendAudio, true);
3622
3582
  }
3623
3583
 
3624
3584
  const checkVideoPublished = () => {
3585
+ assert.calledWith(MediaUtil.createMediaStream, [videoTrack]);
3586
+ assert.calledOnce(LocalCameraTrackConstructorStub);
3587
+
3625
3588
  assert.calledWith(createMuteStateStub, 'video', meeting, meeting.mediaProperties.mediaDirection);
3626
- assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.publishTrack, videoTrack, 'main');
3627
- assert.equal(meeting.mediaProperties.videoTrack, videoTrack);
3589
+ assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.publishTrack, fakeLocalCameraTrack);
3590
+ assert.equal(meeting.mediaProperties.videoTrack, fakeLocalCameraTrack);
3628
3591
  assert.equal(meeting.mediaProperties.mediaDirection.sendVideo, true);
3629
3592
  }
3630
3593
 
3631
3594
  const checkScreenShareVideoPublished = () => {
3632
3595
  assert.calledOnce(meeting.requestScreenShareFloor);
3633
- assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.publishTrack, videoShareTrack, 'slides');
3634
- assert.equal(meeting.mediaProperties.shareTrack, videoShareTrack);
3596
+
3597
+ assert.calledWith(MediaUtil.createMediaStream, [videoShareTrack]);
3598
+ assert.calledOnce(LocalDisplayTrackConstructorStub);
3599
+
3600
+ assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.publishTrack, fakeLocalDisplayTrack);
3601
+ assert.equal(meeting.mediaProperties.shareTrack, fakeLocalDisplayTrack);
3635
3602
  assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
3636
3603
  }
3637
3604
 
@@ -3685,21 +3652,21 @@ describe('plugin-meetings', () => {
3685
3652
  });
3686
3653
 
3687
3654
  const checkAudioUnpublished = () => {
3688
- assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack, audioTrack, 'main');
3655
+ assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack, fakeLocalMicrophoneTrack);
3689
3656
 
3690
3657
  assert.equal(meeting.mediaProperties.audioTrack, null);
3691
3658
  assert.equal(meeting.mediaProperties.mediaDirection.sendAudio, false);
3692
3659
  };
3693
3660
 
3694
3661
  const checkVideoUnpublished = () => {
3695
- assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack, videoTrack, 'main');
3662
+ assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack, fakeLocalCameraTrack);
3696
3663
 
3697
3664
  assert.equal(meeting.mediaProperties.videoTrack, null);
3698
3665
  assert.equal(meeting.mediaProperties.mediaDirection.sendVideo, false);
3699
3666
  }
3700
3667
 
3701
3668
  const checkScreenShareVideoUnpublished = () => {
3702
- assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack, videoShareTrack, 'slides');
3669
+ assert.calledWith(meeting.mediaProperties.webrtcMediaConnection.unpublishTrack, fakeLocalDisplayTrack);
3703
3670
 
3704
3671
  assert.calledOnce(meeting.requestScreenShareFloor);
3705
3672
 
@@ -3924,7 +3891,6 @@ describe('plugin-meetings', () => {
3924
3891
  });
3925
3892
  describe('#setLocalShareTrack', () => {
3926
3893
  it('should trigger a media:ready event with local share stream', () => {
3927
- let registeredListener = null;
3928
3894
  const track = {
3929
3895
  getSettings: sinon.stub().returns({
3930
3896
  aspectRatio: '1.7',
@@ -3934,11 +3900,17 @@ describe('plugin-meetings', () => {
3934
3900
  displaySurface: true,
3935
3901
  cursor: true,
3936
3902
  }),
3937
- addEventListener: sinon.stub().callsFake((event, listener) => {
3938
- registeredListener = listener;
3939
- }),
3940
3903
  };
3941
3904
 
3905
+ const listeners = {};
3906
+ const fakeLocalDisplayTrack = {
3907
+ on: sinon.stub().callsFake((event, listener) => {
3908
+ listeners[event] = listener;
3909
+ })
3910
+ };
3911
+ sinon.stub(InternalMediaCoreModule, 'LocalDisplayTrack').returns(fakeLocalDisplayTrack);
3912
+
3913
+
3942
3914
  meeting.mediaProperties.setLocalShareTrack = sinon.stub().returns(true);
3943
3915
  meeting.stopShare = sinon.stub().resolves(true);
3944
3916
  meeting.mediaProperties.mediaDirection = {};
@@ -3952,8 +3924,8 @@ describe('plugin-meetings', () => {
3952
3924
  );
3953
3925
  assert.calledOnce(meeting.mediaProperties.setLocalShareTrack);
3954
3926
  assert.equal(meeting.mediaProperties.localStream, undefined);
3955
- assert.isNotNull(registeredListener);
3956
- registeredListener();
3927
+ assert.isNotNull(listeners[LocalTrackEvents.Ended]);
3928
+ listeners[LocalTrackEvents.Ended]();
3957
3929
  assert.calledOnce(meeting.stopShare);
3958
3930
  });
3959
3931
  });
@@ -3969,6 +3941,7 @@ describe('plugin-meetings', () => {
3969
3941
  eventListeners[event] = listener;
3970
3942
  }),
3971
3943
  };
3944
+ MediaUtil.createMediaStream.returns({id: 'stream'});
3972
3945
  });
3973
3946
 
3974
3947
  it('should register for all the correct RoapMediaConnection events', () => {
@@ -3990,7 +3963,7 @@ describe('plugin-meetings', () => {
3990
3963
  assert.equal(TriggerProxy.trigger.getCall(1).args[2], 'media:ready');
3991
3964
  assert.deepEqual(TriggerProxy.trigger.getCall(1).args[3], {
3992
3965
  type: 'remoteAudio',
3993
- stream: true,
3966
+ stream: {id: 'stream'},
3994
3967
  });
3995
3968
 
3996
3969
  eventListeners[Event.REMOTE_TRACK_ADDED]({
@@ -4000,7 +3973,7 @@ describe('plugin-meetings', () => {
4000
3973
  assert.equal(TriggerProxy.trigger.getCall(2).args[2], 'media:ready');
4001
3974
  assert.deepEqual(TriggerProxy.trigger.getCall(2).args[3], {
4002
3975
  type: 'remoteVideo',
4003
- stream: true,
3976
+ stream: {id: 'stream'},
4004
3977
  });
4005
3978
 
4006
3979
  eventListeners[Event.REMOTE_TRACK_ADDED]({
@@ -4010,7 +3983,7 @@ describe('plugin-meetings', () => {
4010
3983
  assert.equal(TriggerProxy.trigger.getCall(3).args[2], 'media:ready');
4011
3984
  assert.deepEqual(TriggerProxy.trigger.getCall(3).args[3], {
4012
3985
  type: 'remoteShare',
4013
- stream: true,
3986
+ stream: {id: 'stream'},
4014
3987
  });
4015
3988
  });
4016
3989