@webex/plugin-meetings 3.0.0-stream-classes.4 → 3.0.0-stream-classes.5
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.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +25 -1
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +5 -0
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/meeting/index.js +72 -15
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +1 -1
- package/dist/meeting/request.js.map +1 -1
- package/dist/metrics/constants.js +3 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +46 -9
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/meeting/index.d.ts +16 -2
- package/dist/types/meeting/locusMediaRequest.d.ts +1 -2
- package/dist/types/meeting/request.d.ts +2 -1
- package/dist/types/meeting/util.d.ts +9 -1
- package/dist/types/metrics/constants.d.ts +3 -0
- package/dist/types/multistream/remoteMediaManager.d.ts +9 -1
- package/dist/types/reachability/index.d.ts +0 -6
- package/dist/types/reachability/request.d.ts +1 -1
- package/dist/types/roap/request.d.ts +2 -1
- package/package.json +1 -1
- package/src/constants.ts +1 -0
- package/src/locus-info/index.ts +33 -2
- package/src/locus-info/parser.ts +7 -0
- package/src/meeting/index.ts +59 -16
- package/src/meeting/request.ts +1 -1
- package/src/metrics/constants.ts +2 -0
- package/src/multistream/remoteMediaManager.ts +41 -4
- package/test/integration/spec/converged-space-meetings.js +7 -7
- package/test/integration/spec/journey.js +85 -103
- package/test/integration/spec/space-meeting.js +9 -9
- package/test/unit/spec/locus-info/index.js +118 -1
- package/test/unit/spec/meeting/index.js +88 -25
- package/test/unit/spec/meetings/index.js +2 -2
- package/test/unit/spec/multistream/remoteMediaManager.ts +10 -2
- package/test/utils/integrationTestUtils.js +4 -4
|
@@ -6,10 +6,11 @@ import {skipInNode} from '@webex/test-helper-mocha';
|
|
|
6
6
|
import sinon from 'sinon';
|
|
7
7
|
|
|
8
8
|
import BrowserDetection from '@webex/plugin-meetings/dist/common/browser-detection';
|
|
9
|
-
import {
|
|
9
|
+
import {createCameraStream, createDisplayStream, createMicrophoneStream, LocalTrackEvents, LocalStreamEventNames} from '@webex/plugin-meetings';
|
|
10
10
|
|
|
11
11
|
import testUtils from '../../utils/testUtils';
|
|
12
12
|
import integrationTestUtils from '../../utils/integrationTestUtils';
|
|
13
|
+
import {EVENT_TRIGGERS} from '../../../src/constants';
|
|
13
14
|
|
|
14
15
|
require('dotenv').config();
|
|
15
16
|
|
|
@@ -19,7 +20,7 @@ const {isBrowser} = BrowserDetection();
|
|
|
19
20
|
|
|
20
21
|
let userSet, alice, bob, chris, enumerateSpy, channelUrlA, channelUrlB;
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
+
const localStreams = {
|
|
23
24
|
alice: {
|
|
24
25
|
microphone: undefined,
|
|
25
26
|
camera: undefined,
|
|
@@ -44,13 +45,13 @@ const localTracks = {
|
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
const waitForPublished = (
|
|
48
|
+
const waitForPublished = (meeting, expectedPublished, description) => {
|
|
48
49
|
return testUtils.waitForEvents([{
|
|
49
|
-
scope:
|
|
50
|
-
event:
|
|
51
|
-
match: (
|
|
52
|
-
console.log(`${description} is now ${isPublished ? 'published': 'not published'}`);
|
|
53
|
-
return (isPublished === expectedPublished);
|
|
50
|
+
scope: meeting,
|
|
51
|
+
event: EVENT_TRIGGERS.MEETING_STREAM_PUBLISH_STATE_CHANGED,
|
|
52
|
+
match: (event) => {
|
|
53
|
+
console.log(`${description} is now ${event.isPublished ? 'published': 'not published'}`);
|
|
54
|
+
return (event.isPublished === expectedPublished);
|
|
54
55
|
}
|
|
55
56
|
}]);
|
|
56
57
|
};
|
|
@@ -312,8 +313,8 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
312
313
|
});
|
|
313
314
|
|
|
314
315
|
it('alice creates local microphone and camera tracks', async () => {
|
|
315
|
-
|
|
316
|
-
|
|
316
|
+
localStreams.alice.microphone = await createMicrophoneStream();
|
|
317
|
+
localStreams.alice.camera = await createCameraStream();
|
|
317
318
|
});
|
|
318
319
|
|
|
319
320
|
it('alice dials bob and adds media', () =>
|
|
@@ -343,7 +344,7 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
343
344
|
})
|
|
344
345
|
.then(() =>
|
|
345
346
|
Promise.all([
|
|
346
|
-
integrationTestUtils.addMedia(alice, {microphone:
|
|
347
|
+
integrationTestUtils.addMedia(alice, {microphone: localStreams.alice.microphone, camera: localStreams.alice.camera}),
|
|
347
348
|
testUtils.waitForEvents([
|
|
348
349
|
{scope: alice.meeting, event: 'meeting:media:local:start', user: alice},
|
|
349
350
|
]),
|
|
@@ -371,13 +372,13 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
371
372
|
});
|
|
372
373
|
|
|
373
374
|
it('bob creates local microphone and camera tracks', async () => {
|
|
374
|
-
|
|
375
|
-
|
|
375
|
+
localStreams.bob.microphone = await createMicrophoneStream();
|
|
376
|
+
localStreams.bob.camera = await createCameraStream();
|
|
376
377
|
});
|
|
377
378
|
|
|
378
379
|
it('bob adds media to the meeting', () =>
|
|
379
380
|
Promise.all([
|
|
380
|
-
integrationTestUtils.addMedia(bob, {microphone:
|
|
381
|
+
integrationTestUtils.addMedia(bob, {microphone: localStreams.bob.microphone, camera: localStreams.bob.camera}),
|
|
381
382
|
testUtils
|
|
382
383
|
.waitForEvents([
|
|
383
384
|
{scope: bob.meeting, event: 'meeting:media:local:start', user: bob},
|
|
@@ -431,11 +432,11 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
431
432
|
{scope: bob.meeting.members, event: 'members:update', match: checkEvent},
|
|
432
433
|
]);
|
|
433
434
|
|
|
434
|
-
|
|
435
|
+
localStreams.alice.microphone.setMuted(true);
|
|
435
436
|
|
|
436
437
|
await membersUpdate;
|
|
437
438
|
|
|
438
|
-
assert.equal(
|
|
439
|
+
assert.equal(localStreams.alice.microphone.muted, true);
|
|
439
440
|
});
|
|
440
441
|
|
|
441
442
|
it('alice Audio unMute ', async () => {
|
|
@@ -450,11 +451,11 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
450
451
|
{scope: bob.meeting.members, event: 'members:update', match: checkEvent},
|
|
451
452
|
]);
|
|
452
453
|
|
|
453
|
-
|
|
454
|
+
localStreams.alice.microphone.setMuted(false);
|
|
454
455
|
|
|
455
456
|
await membersUpdate;
|
|
456
457
|
|
|
457
|
-
assert.equal(
|
|
458
|
+
assert.equal(localStreams.alice.microphone.muted, false);
|
|
458
459
|
});
|
|
459
460
|
|
|
460
461
|
it('alice video mute', async () => {
|
|
@@ -469,11 +470,11 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
469
470
|
{scope: bob.meeting.members, event: 'members:update', match: checkEvent},
|
|
470
471
|
]);
|
|
471
472
|
|
|
472
|
-
|
|
473
|
+
localStreams.alice.camera.setMuted(true);
|
|
473
474
|
|
|
474
475
|
await membersUpdate;
|
|
475
476
|
|
|
476
|
-
assert.equal(
|
|
477
|
+
assert.equal(localStreams.alice.camera.muted, true);
|
|
477
478
|
});
|
|
478
479
|
|
|
479
480
|
it('alice video unmute', async () => {
|
|
@@ -488,77 +489,57 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
488
489
|
{scope: bob.meeting.members, event: 'members:update', match: checkEvent},
|
|
489
490
|
]);
|
|
490
491
|
|
|
491
|
-
|
|
492
|
+
localStreams.alice.camera.setMuted(false);
|
|
492
493
|
|
|
493
494
|
await membersUpdate;
|
|
494
495
|
|
|
495
|
-
assert.equal(
|
|
496
|
+
assert.equal(localStreams.alice.camera.muted, false);
|
|
496
497
|
});
|
|
497
498
|
|
|
498
499
|
it('alice update Audio', async () => {
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
const oldMicrophoneTrack = localTracks.alice.microphone;
|
|
502
|
-
const newMicrophoneTrack = await createMicrophoneTrack();
|
|
503
|
-
|
|
504
|
-
assert.equal(oldMicrophoneTrack.published, true);
|
|
505
|
-
assert.notEqual(oldMicrophoneTrack.id, newMicrophoneTrack.id);
|
|
506
|
-
|
|
507
|
-
const oldTrackUnpublished = waitForPublished(oldMicrophoneTrack, false, "Alice AUDIO: old microphone track");
|
|
508
|
-
const newTrackPublished = waitForPublished(newMicrophoneTrack, true, "Alice AUDIO: new microphone track");
|
|
500
|
+
const newMicrophoneStream = await createMicrophoneStream();
|
|
501
|
+
const newStreamPublished = waitForPublished(alice.meeting, true, "Alice AUDIO: new microphone stream");
|
|
509
502
|
|
|
510
503
|
await testUtils.delayedPromise(
|
|
511
504
|
alice.meeting
|
|
512
|
-
.
|
|
513
|
-
microphone:
|
|
505
|
+
.publishStreams({
|
|
506
|
+
microphone: newMicrophoneStream,
|
|
514
507
|
})
|
|
515
508
|
.then(() => {
|
|
516
|
-
console.log('Alice AUDIO: new
|
|
509
|
+
console.log('Alice AUDIO: new stream on meeting object:', alice.meeting.mediaProperties.audioStream);
|
|
517
510
|
assert.equal(
|
|
518
|
-
alice.meeting.mediaProperties.
|
|
519
|
-
|
|
511
|
+
alice.meeting.mediaProperties.audioStream.id,
|
|
512
|
+
newMicrophoneStream.id
|
|
520
513
|
);
|
|
521
|
-
assert.equal(alice.meeting.mediaProperties.videoTrack.id, oldVideoTrackId);
|
|
522
514
|
})
|
|
523
515
|
);
|
|
524
516
|
|
|
525
|
-
await
|
|
526
|
-
await newTrackPublished;
|
|
517
|
+
await newStreamPublished;
|
|
527
518
|
|
|
528
|
-
|
|
519
|
+
localStreams.alice.microphone = newMicrophoneStream;
|
|
529
520
|
});
|
|
530
521
|
|
|
531
522
|
it('alice update video', async () => {
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
const oldCameraTrack = localTracks.alice.camera;
|
|
535
|
-
const newCameraTrack = await createCameraTrack();
|
|
536
|
-
|
|
537
|
-
assert.equal(oldCameraTrack.published, true);
|
|
538
|
-
assert.notEqual(oldCameraTrack.id, newCameraTrack.id);
|
|
539
|
-
|
|
540
|
-
const oldTrackUnpublished = waitForPublished(oldCameraTrack, false, "Alice VIDEO: old camera track");
|
|
541
|
-
const newTrackPublished = waitForPublished(newCameraTrack, true, "Alice VIDEO: new camera track");
|
|
523
|
+
const newCameraStream = await createCameraStream();
|
|
524
|
+
const newStreamPublished = waitForPublished(alice.meeting, true, "Alice VIDEO: new camera stream");
|
|
542
525
|
|
|
543
526
|
await testUtils.delayedPromise(
|
|
544
527
|
alice.meeting
|
|
545
|
-
.
|
|
546
|
-
camera:
|
|
528
|
+
.publishStreams({
|
|
529
|
+
camera: newCameraStream,
|
|
547
530
|
})
|
|
548
531
|
.then(() => {
|
|
549
|
-
console.log('Alice VIDEO: new
|
|
532
|
+
console.log('Alice VIDEO: new stream on meeting:', alice.meeting.mediaProperties.videoStream);
|
|
550
533
|
assert.equal(
|
|
551
|
-
alice.meeting.mediaProperties.
|
|
552
|
-
|
|
534
|
+
alice.meeting.mediaProperties.videoStream.id,
|
|
535
|
+
newCameraStream.id
|
|
553
536
|
);
|
|
554
|
-
assert.equal(alice.meeting.mediaProperties.audioTrack.id, oldAudioTrackId);
|
|
555
537
|
})
|
|
556
538
|
);
|
|
557
539
|
|
|
558
|
-
await
|
|
559
|
-
await newTrackPublished;
|
|
540
|
+
await newStreamPublished;
|
|
560
541
|
|
|
561
|
-
|
|
542
|
+
localStreams.alice.camera = newCameraStream;
|
|
562
543
|
});
|
|
563
544
|
|
|
564
545
|
it('alice mutes bob', () =>
|
|
@@ -593,11 +574,11 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
593
574
|
]);
|
|
594
575
|
|
|
595
576
|
// first bob mutes himself
|
|
596
|
-
|
|
577
|
+
localStreams.bob.microphone.setMuted(true);
|
|
597
578
|
|
|
598
579
|
await membersUpdate;
|
|
599
580
|
|
|
600
|
-
assert.equal(
|
|
581
|
+
assert.equal(localStreams.bob.microphone.muted, true);
|
|
601
582
|
|
|
602
583
|
// now alice tries to unmmute bob
|
|
603
584
|
await testUtils.delayedPromise(alice.meeting.mute(bob.meeting.members.selfId, false))
|
|
@@ -612,7 +593,7 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
612
593
|
assert.fail('bob received unexpected meeting:self:unmutedByOthers event');
|
|
613
594
|
})
|
|
614
595
|
.catch(() => {
|
|
615
|
-
assert.equal(
|
|
596
|
+
assert.equal(localStreams.bob.microphone.muted, true);
|
|
616
597
|
});
|
|
617
598
|
});
|
|
618
599
|
|
|
@@ -626,15 +607,15 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
626
607
|
{scope: alice.meeting.members, event: 'members:update', match: checkEvent},
|
|
627
608
|
]);
|
|
628
609
|
|
|
629
|
-
|
|
610
|
+
localStreams.bob.microphone.setMuted(false);
|
|
630
611
|
|
|
631
612
|
await membersUpdate;
|
|
632
613
|
|
|
633
|
-
assert.equal(
|
|
614
|
+
assert.equal(localStreams.bob.microphone.muted, false);
|
|
634
615
|
});
|
|
635
616
|
|
|
636
617
|
it('alice shares the screen with highFrameRate', async () => {
|
|
637
|
-
|
|
618
|
+
localStreams.alice.screenShare.video = await createDisplayStream();
|
|
638
619
|
|
|
639
620
|
const startedSharingLocal = testUtils.waitForEvents([{scope: alice.meeting, event: 'meeting:startedSharingLocal'}]);
|
|
640
621
|
const startedSharingRemote = testUtils.waitForEvents([{scope: bob.meeting, event: 'meeting:startedSharingRemote'}])
|
|
@@ -648,9 +629,10 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
648
629
|
JSON.stringify(response, testUtils.getCircularReplacer())
|
|
649
630
|
);
|
|
650
631
|
});
|
|
651
|
-
const screenShareVideoPublished = waitForPublished(localTracks.alice.screenShare.video, true, "alice's screen share video track");
|
|
652
632
|
|
|
653
|
-
|
|
633
|
+
const screenShareVideoPublished = waitForPublished(alice.meeting, true, "alice's screen share video stream");
|
|
634
|
+
|
|
635
|
+
await testUtils.delayedPromise(alice.meeting.publishStreams({screenShare: {video: localStreams.alice.screenShare.video}}));
|
|
654
636
|
|
|
655
637
|
await screenShareVideoPublished;
|
|
656
638
|
await startedSharingLocal;
|
|
@@ -669,7 +651,7 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
669
651
|
});
|
|
670
652
|
|
|
671
653
|
it('bob steals the screen share from alice', async () => {
|
|
672
|
-
|
|
654
|
+
localStreams.bob.screenShare.video = await createDisplayStream();
|
|
673
655
|
|
|
674
656
|
const stoppedSharingLocal = testUtils.waitForEvents([{scope: alice.meeting, event: 'meeting:stoppedSharingLocal'}]);
|
|
675
657
|
const startedSharingLocal = testUtils.waitForEvents([{scope: bob.meeting, event: 'meeting:startedSharingLocal'}]);
|
|
@@ -684,10 +666,10 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
684
666
|
JSON.stringify(response, testUtils.getCircularReplacer())
|
|
685
667
|
);
|
|
686
668
|
});
|
|
687
|
-
const aliceScreenShareVideoUnpublished = waitForPublished(
|
|
688
|
-
const bobScreenShareVideoPublished = waitForPublished(
|
|
669
|
+
const aliceScreenShareVideoUnpublished = waitForPublished(alice.meeting, false, "alice's screen share video stream");
|
|
670
|
+
const bobScreenShareVideoPublished = waitForPublished(bob.meeting, true, "bob's screen share video stream");
|
|
689
671
|
|
|
690
|
-
await testUtils.delayedPromise(bob.meeting.
|
|
672
|
+
await testUtils.delayedPromise(bob.meeting.publishStreams({screenShare: {video: localStreams.bob.screenShare.video}}));
|
|
691
673
|
|
|
692
674
|
await bobScreenShareVideoPublished;
|
|
693
675
|
await aliceScreenShareVideoUnpublished;
|
|
@@ -696,8 +678,8 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
696
678
|
await startedSharingRemote;
|
|
697
679
|
await aliceReceivesMembersUpdate;
|
|
698
680
|
|
|
699
|
-
|
|
700
|
-
|
|
681
|
+
localStreams.alice.screenShare.video.stop();
|
|
682
|
+
localStreams.alice.screenShare.video = undefined;
|
|
701
683
|
|
|
702
684
|
assert.equal(bob.meeting.screenShareFloorState, 'floor_request_granted');
|
|
703
685
|
assert.equal(bob.meeting.shareStatus, 'local_share_active');
|
|
@@ -707,18 +689,18 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
707
689
|
});
|
|
708
690
|
|
|
709
691
|
it('bob stops sharing', async () => {
|
|
710
|
-
const screenShareVideoUnpublished = waitForPublished(
|
|
692
|
+
const screenShareVideoUnpublished = waitForPublished(bob.meeting, false, "bob's screen share video stream");
|
|
711
693
|
const stoppedSharingLocal = testUtils.waitForEvents([{scope: bob.meeting, event: 'meeting:stoppedSharingLocal'}]);
|
|
712
694
|
const stoppedSharingRemote = testUtils.waitForEvents([{scope: alice.meeting, event: 'meeting:stoppedSharingRemote'}]);
|
|
713
695
|
|
|
714
|
-
await testUtils.delayedPromise(bob.meeting.
|
|
696
|
+
await testUtils.delayedPromise(bob.meeting.unpublishStreams([localStreams.bob.screenShare.video]));
|
|
715
697
|
|
|
716
698
|
await screenShareVideoUnpublished;
|
|
717
699
|
await stoppedSharingLocal;
|
|
718
700
|
await stoppedSharingRemote;
|
|
719
701
|
|
|
720
|
-
|
|
721
|
-
|
|
702
|
+
localStreams.bob.screenShare.video.stop();
|
|
703
|
+
localStreams.bob.screenShare.video = undefined;
|
|
722
704
|
|
|
723
705
|
assert.equal(bob.meeting.screenShareFloorState, 'floor_released');
|
|
724
706
|
assert.equal(bob.meeting.shareStatus, 'no_share');
|
|
@@ -829,7 +811,7 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
829
811
|
}));
|
|
830
812
|
|
|
831
813
|
it('bob steals the share from alice with desktop share', async () => {
|
|
832
|
-
|
|
814
|
+
localStreams.bob.screenShare.video = await createDisplayStream();
|
|
833
815
|
|
|
834
816
|
const stoppedSharingWhiteboard = testUtils.waitForEvents([{scope: alice.meeting, event: 'meeting:stoppedSharingWhiteboard'}]);
|
|
835
817
|
const startedSharingLocal = testUtils.waitForEvents([{scope: bob.meeting, event: 'meeting:startedSharingLocal'}]);
|
|
@@ -844,9 +826,9 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
844
826
|
JSON.stringify(response, testUtils.getCircularReplacer())
|
|
845
827
|
);
|
|
846
828
|
});
|
|
847
|
-
const bobScreenShareVideoPublished = waitForPublished(
|
|
829
|
+
const bobScreenShareVideoPublished = waitForPublished(bob.meeting, true, "bob's screen share video stream");
|
|
848
830
|
|
|
849
|
-
await testUtils.delayedPromise(bob.meeting.
|
|
831
|
+
await testUtils.delayedPromise(bob.meeting.publishStreams({screenShare: {video: localStreams.bob.screenShare.video}}));
|
|
850
832
|
|
|
851
833
|
await bobScreenShareVideoPublished;
|
|
852
834
|
await stoppedSharingWhiteboard;
|
|
@@ -931,10 +913,10 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
931
913
|
})
|
|
932
914
|
.then(() => testUtils.waitForStateChange(chris.meeting, 'JOINED'))
|
|
933
915
|
.then(async () => {
|
|
934
|
-
|
|
935
|
-
|
|
916
|
+
localStreams.chris.microphone = await createMicrophoneStream();
|
|
917
|
+
localStreams.chris.camera = await createCameraStream();
|
|
936
918
|
})
|
|
937
|
-
.then(() => integrationTestUtils.addMedia(chris, {microphone:
|
|
919
|
+
.then(() => integrationTestUtils.addMedia(chris, {microphone: localStreams.chris.microphone, camera: localStreams.chris.camera}))
|
|
938
920
|
.then(() => assert(enumerateSpy.called));
|
|
939
921
|
})
|
|
940
922
|
.then(() =>
|
|
@@ -982,32 +964,32 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
982
964
|
});
|
|
983
965
|
});
|
|
984
966
|
|
|
985
|
-
it('stop all local
|
|
986
|
-
if (
|
|
987
|
-
|
|
988
|
-
|
|
967
|
+
it('stop all local streams', () => {
|
|
968
|
+
if (localStreams.alice.microphone) {
|
|
969
|
+
localStreams.alice.microphone.stop();
|
|
970
|
+
localStreams.alice.microphone = undefined;
|
|
989
971
|
}
|
|
990
|
-
if (
|
|
991
|
-
|
|
992
|
-
|
|
972
|
+
if (localStreams.alice.camera) {
|
|
973
|
+
localStreams.alice.camera.stop();
|
|
974
|
+
localStreams.alice.camera = undefined;
|
|
993
975
|
}
|
|
994
976
|
|
|
995
|
-
if (
|
|
996
|
-
|
|
997
|
-
|
|
977
|
+
if (localStreams.bob.microphone) {
|
|
978
|
+
localStreams.bob.microphone.stop();
|
|
979
|
+
localStreams.bob.microphone = undefined;
|
|
998
980
|
}
|
|
999
|
-
if (
|
|
1000
|
-
|
|
1001
|
-
|
|
981
|
+
if (localStreams.bob.camera) {
|
|
982
|
+
localStreams.bob.camera.stop();
|
|
983
|
+
localStreams.bob.camera = undefined;
|
|
1002
984
|
}
|
|
1003
985
|
|
|
1004
|
-
if (
|
|
1005
|
-
|
|
1006
|
-
|
|
986
|
+
if (localStreams.chris.microphone) {
|
|
987
|
+
localStreams.chris.microphone.stop();
|
|
988
|
+
localStreams.chris.microphone = undefined;
|
|
1007
989
|
}
|
|
1008
|
-
if (
|
|
1009
|
-
|
|
1010
|
-
|
|
990
|
+
if (localStreams.chris.camera) {
|
|
991
|
+
localStreams.chris.camera.stop();
|
|
992
|
+
localStreams.chris.camera = undefined;
|
|
1011
993
|
}
|
|
1012
994
|
});
|
|
1013
995
|
});
|
|
@@ -3,7 +3,7 @@ import {assert} from '@webex/test-helper-chai';
|
|
|
3
3
|
import {skipInNode, jenkinsOnly} from '@webex/test-helper-mocha';
|
|
4
4
|
import {patterns} from '@webex/common';
|
|
5
5
|
import MeetingInfoUtil from '@webex/plugin-meetings/dist/meeting-info/utilv2';
|
|
6
|
-
import {
|
|
6
|
+
import {createCameraStream, createMicrophoneStream} from '@webex/plugin-meetings';
|
|
7
7
|
|
|
8
8
|
import CMR from '../../utils/cmr';
|
|
9
9
|
import testUtils from '../../utils/testUtils';
|
|
@@ -153,11 +153,11 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
153
153
|
.then(() => testUtils.waitForStateChange(chris.meeting, 'JOINED')));
|
|
154
154
|
|
|
155
155
|
it('Bob and Alice create local microphone and camera tracks', async () => {
|
|
156
|
-
localTracks.alice.microphone = await
|
|
157
|
-
localTracks.alice.camera = await
|
|
156
|
+
localTracks.alice.microphone = await createMicrophoneStream();
|
|
157
|
+
localTracks.alice.camera = await createCameraStream();
|
|
158
158
|
|
|
159
|
-
localTracks.bob.microphone = await
|
|
160
|
-
localTracks.bob.camera = await
|
|
159
|
+
localTracks.bob.microphone = await createMicrophoneStream();
|
|
160
|
+
localTracks.bob.camera = await createCameraStream();
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
it('Bob and Alice addsMedia', () =>
|
|
@@ -214,8 +214,8 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
214
214
|
)
|
|
215
215
|
.then(() => testUtils.waitForStateChange(guest.meeting, 'JOINED'))
|
|
216
216
|
.then(async () => {
|
|
217
|
-
localTracks.guest.microphone = await
|
|
218
|
-
localTracks.guest.camera = await
|
|
217
|
+
localTracks.guest.microphone = await createMicrophoneStream();
|
|
218
|
+
localTracks.guest.camera = await createCameraStream();
|
|
219
219
|
})
|
|
220
220
|
.then(() => integrationTestUtils.addMedia(guest, {microphone: localTracks.guest.microphone, camera: localTracks.guest.camera}))
|
|
221
221
|
.catch((e) => {
|
|
@@ -474,8 +474,8 @@ skipInNode(describe)('plugin-meetings', () => {
|
|
|
474
474
|
])
|
|
475
475
|
.then(() => testUtils.waitForStateChange(guest.meeting, 'JOINED'))
|
|
476
476
|
.then(async () => {
|
|
477
|
-
localTracks.guest.microphone = await
|
|
478
|
-
localTracks.guest.camera = await
|
|
477
|
+
localTracks.guest.microphone = await createMicrophoneStream();
|
|
478
|
+
localTracks.guest.camera = await createCameraStream();
|
|
479
479
|
})
|
|
480
480
|
.then(() => integrationTestUtils.addMedia(guest, {microphone: localTracks.guest.microphone, camera: localTracks.guest.camera}));
|
|
481
481
|
})
|
|
@@ -10,6 +10,7 @@ import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
|
|
|
10
10
|
import InfoUtils from '@webex/plugin-meetings/src/locus-info/infoUtils';
|
|
11
11
|
import EmbeddedAppsUtils from '@webex/plugin-meetings/src/locus-info/embeddedAppsUtils';
|
|
12
12
|
import LocusDeltaParser from '@webex/plugin-meetings/src/locus-info/parser';
|
|
13
|
+
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
13
14
|
|
|
14
15
|
import {
|
|
15
16
|
LOCUSINFO,
|
|
@@ -24,7 +25,6 @@ import {
|
|
|
24
25
|
} from '../../../../src/constants';
|
|
25
26
|
|
|
26
27
|
import {self, selfWithInactivity} from './selfConstant';
|
|
27
|
-
import uuid from 'uuid';
|
|
28
28
|
|
|
29
29
|
describe('plugin-meetings', () => {
|
|
30
30
|
describe('LocusInfo index', () => {
|
|
@@ -39,6 +39,7 @@ describe('plugin-meetings', () => {
|
|
|
39
39
|
const locus = {};
|
|
40
40
|
const meetingId = 'meetingId';
|
|
41
41
|
let locusInfo;
|
|
42
|
+
let sendBehavioralMetricStub;
|
|
42
43
|
|
|
43
44
|
const webex = new MockWebex({
|
|
44
45
|
children: {
|
|
@@ -65,6 +66,12 @@ describe('plugin-meetings', () => {
|
|
|
65
66
|
},
|
|
66
67
|
},
|
|
67
68
|
};
|
|
69
|
+
|
|
70
|
+
sendBehavioralMetricStub = sinon.stub(Metrics, 'sendBehavioralMetric');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
sinon.restore();
|
|
68
75
|
});
|
|
69
76
|
|
|
70
77
|
describe('#updateControls', () => {
|
|
@@ -1877,6 +1884,114 @@ describe('plugin-meetings', () => {
|
|
|
1877
1884
|
});
|
|
1878
1885
|
});
|
|
1879
1886
|
|
|
1887
|
+
describe('edge cases for sync failing', () => {
|
|
1888
|
+
const {DESYNC} = LocusDeltaParser.loci;
|
|
1889
|
+
const fakeFullLocusDto = {id: 'fake full locus dto'};
|
|
1890
|
+
let meeting;
|
|
1891
|
+
|
|
1892
|
+
beforeEach(() => {
|
|
1893
|
+
sinon.stub(locusInfo.locusParser, 'resume');
|
|
1894
|
+
sinon.stub(webex.meetings, 'destroy');
|
|
1895
|
+
|
|
1896
|
+
meeting = {
|
|
1897
|
+
meetingRequest: {
|
|
1898
|
+
getLocusDTO: sandbox.stub(),
|
|
1899
|
+
},
|
|
1900
|
+
locusInfo: {
|
|
1901
|
+
handleLocusDelta: sandbox.stub(),
|
|
1902
|
+
onFullLocus: sandbox.stub(),
|
|
1903
|
+
},
|
|
1904
|
+
locusUrl: 'fullSyncUrl',
|
|
1905
|
+
};
|
|
1906
|
+
|
|
1907
|
+
locusInfo.locusParser.workingCopy = {
|
|
1908
|
+
syncUrl: 'deltaSyncUrl',
|
|
1909
|
+
};
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1912
|
+
it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl and destroys the meeting if that fails', () => {
|
|
1913
|
+
meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
|
|
1914
|
+
|
|
1915
|
+
locusInfo.locusParser.workingCopy = {}; // no syncUrl
|
|
1916
|
+
|
|
1917
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1918
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1919
|
+
return new Promise((resolve) => {
|
|
1920
|
+
webex.meetings.destroy.callsFake(() => resolve());
|
|
1921
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1922
|
+
}).then(() => {
|
|
1923
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'fullSyncUrl'});
|
|
1924
|
+
|
|
1925
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
1926
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
|
1927
|
+
assert.notCalled(locusInfo.locusParser.resume);
|
|
1928
|
+
|
|
1929
|
+
assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
|
|
1930
|
+
});
|
|
1931
|
+
});
|
|
1932
|
+
|
|
1933
|
+
it('applyLocusDeltaData first tries a delta sync on DESYNC action and if that fails, does a full locus sync', () => {
|
|
1934
|
+
meeting.meetingRequest.getLocusDTO.onCall(0).rejects(new Error('fake error'));
|
|
1935
|
+
meeting.meetingRequest.getLocusDTO.onCall(1).resolves({body: fakeFullLocusDto});
|
|
1936
|
+
|
|
1937
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1938
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1939
|
+
return new Promise((resolve) => {
|
|
1940
|
+
locusInfo.locusParser.resume.callsFake(() => resolve());
|
|
1941
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1942
|
+
}).then(() => {
|
|
1943
|
+
assert.calledTwice(meeting.meetingRequest.getLocusDTO);
|
|
1944
|
+
|
|
1945
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
|
|
1946
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
|
|
1947
|
+
|
|
1948
|
+
assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
|
|
1949
|
+
correlationId: meeting.correlationId,
|
|
1950
|
+
url: 'deltaSyncUrl',
|
|
1951
|
+
reason: 'fake error',
|
|
1952
|
+
errorName: 'Error',
|
|
1953
|
+
stack: sinon.match.any,
|
|
1954
|
+
code: sinon.match.any,
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
1958
|
+
assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
|
|
1959
|
+
assert.calledOnce(locusInfo.locusParser.resume);
|
|
1960
|
+
});
|
|
1961
|
+
});
|
|
1962
|
+
|
|
1963
|
+
it('applyLocusDeltaData destroys the meeting if both delta sync and full sync fail', () => {
|
|
1964
|
+
meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
|
|
1965
|
+
|
|
1966
|
+
// Since we have a promise inside a function we want to test that's not returned,
|
|
1967
|
+
// we will wait and stub it's last function to resolve this waiting promise.
|
|
1968
|
+
return new Promise((resolve) => {
|
|
1969
|
+
webex.meetings.destroy.callsFake(() => resolve());
|
|
1970
|
+
locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
|
|
1971
|
+
}).then(() => {
|
|
1972
|
+
assert.calledTwice(meeting.meetingRequest.getLocusDTO);
|
|
1973
|
+
|
|
1974
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
|
|
1975
|
+
assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
|
|
1976
|
+
|
|
1977
|
+
assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
|
|
1978
|
+
correlationId: meeting.correlationId,
|
|
1979
|
+
url: 'deltaSyncUrl',
|
|
1980
|
+
reason: 'fake error',
|
|
1981
|
+
errorName: 'Error',
|
|
1982
|
+
stack: sinon.match.any,
|
|
1983
|
+
code: sinon.match.any,
|
|
1984
|
+
});
|
|
1985
|
+
|
|
1986
|
+
assert.notCalled(meeting.locusInfo.handleLocusDelta);
|
|
1987
|
+
assert.notCalled(meeting.locusInfo.onFullLocus);
|
|
1988
|
+
assert.notCalled(locusInfo.locusParser.resume);
|
|
1989
|
+
|
|
1990
|
+
assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
|
|
1991
|
+
});
|
|
1992
|
+
});
|
|
1993
|
+
});
|
|
1994
|
+
|
|
1880
1995
|
it('onDeltaLocus handle delta data', () => {
|
|
1881
1996
|
fakeLocus.participants = {};
|
|
1882
1997
|
const fakeBreakout = {
|
|
@@ -2420,6 +2535,8 @@ describe('plugin-meetings', () => {
|
|
|
2420
2535
|
// send an out-of-order delta
|
|
2421
2536
|
locusInfo.handleLocusDelta(oooDelta, mockMeeting);
|
|
2422
2537
|
|
|
2538
|
+
assert.calledOnceWithExactly(sendBehavioralMetricStub, 'js_sdk_locus_delta_ooo', { stack: sinon.match.any})
|
|
2539
|
+
|
|
2423
2540
|
await clock.tickAsync(12499);
|
|
2424
2541
|
await testUtils.flushPromises();
|
|
2425
2542
|
assert.notCalled(syncRequestStub);
|