Преглед на файлове

feat(tests): Add start muted test.

factor2
Hristo Terezov преди 8 месеца
родител
ревизия
3e1adcd9b7

+ 129
- 65
tests/helpers/Participant.ts Целия файл

@@ -1,6 +1,7 @@
1 1
 /* global APP $ */
2 2
 
3 3
 import { multiremotebrowser } from '@wdio/globals';
4
+import assert from 'assert';
4 5
 import { Key } from 'webdriverio';
5 6
 
6 7
 import { IConfig } from '../../react/features/base/config/configType';
@@ -10,6 +11,7 @@ import ChatPanel from '../pageobjects/ChatPanel';
10 11
 import Filmstrip from '../pageobjects/Filmstrip';
11 12
 import IframeAPI from '../pageobjects/IframeAPI';
12 13
 import InviteDialog from '../pageobjects/InviteDialog';
14
+import LargeVideo from '../pageobjects/LargeVideo';
13 15
 import LobbyScreen from '../pageobjects/LobbyScreen';
14 16
 import Notifications from '../pageobjects/Notifications';
15 17
 import ParticipantsPane from '../pageobjects/ParticipantsPane';
@@ -27,6 +29,13 @@ export const P2_DISPLAY_NAME = 'p2';
27 29
 export const P3_DISPLAY_NAME = 'p3';
28 30
 export const P4_DISPLAY_NAME = 'p4';
29 31
 
32
+interface IWaitForSendReceiveDataOptions {
33
+    checkReceive?: boolean;
34
+    checkSend?: boolean;
35
+    msg?: string;
36
+    timeout?: number;
37
+}
38
+
30 39
 /**
31 40
  * Participant.
32 41
  */
@@ -91,7 +100,7 @@ export class Participant {
91 100
     async getEndpointId(): Promise<string> {
92 101
         if (!this._endpointId) {
93 102
             this._endpointId = await this.driver.execute(() => { // eslint-disable-line arrow-body-style
94
-                return APP.conference.getMyUserId();
103
+                return APP?.conference?.getMyUserId();
95 104
             });
96 105
         }
97 106
 
@@ -209,7 +218,7 @@ export class Participant {
209 218
         const parallel = [];
210 219
 
211 220
         parallel.push(driver.execute((name, sessionId, prefix) => {
212
-            APP.UI.dockToolbar(true);
221
+            APP?.UI?.dockToolbar(true);
213 222
 
214 223
             // disable keyframe animations (.fadeIn and .fadeOut classes)
215 224
             $('<style>.notransition * { '
@@ -274,8 +283,8 @@ export class Participant {
274 283
     /**
275 284
      * Checks if the participant is in the meeting.
276 285
      */
277
-    async isInMuc() {
278
-        return await this.driver.execute(() => typeof APP !== 'undefined' && APP.conference?.isJoined());
286
+    isInMuc() {
287
+        return this.driver.execute(() => typeof APP !== 'undefined' && APP.conference?.isJoined());
279 288
     }
280 289
 
281 290
     /**
@@ -326,7 +335,7 @@ export class Participant {
326 335
         const driver = this.driver;
327 336
 
328 337
         return driver.waitUntil(() =>
329
-            driver.execute(() => APP.conference.getConnectionState() === 'connected'), {
338
+            driver.execute(() => APP?.conference?.getConnectionState() === 'connected'), {
330 339
             timeout: 15_000,
331 340
             timeoutMsg: `expected ICE to be connected for 15s for ${this.name}`
332 341
         });
@@ -335,47 +344,35 @@ export class Participant {
335 344
     /**
336 345
      * Waits for send and receive data.
337 346
      *
347
+     * @param {Object} options
348
+     * @param {boolean} options.checkSend - If true we will chec
338 349
      * @returns {Promise<void>}
339 350
      */
340
-    async waitForSendReceiveData(
341
-            timeout = 15_000, msg = `expected to receive/send data in 15s for ${this.name}`): Promise<void> {
342
-        const driver = this.driver;
351
+    waitForSendReceiveData({
352
+        checkSend = true,
353
+        checkReceive = true,
354
+        timeout = 15_000,
355
+        msg
356
+    } = {} as IWaitForSendReceiveDataOptions): Promise<void> {
357
+        if (!checkSend && !checkReceive) {
358
+            return Promise.resolve();
359
+        }
360
+
361
+        const lMsg = msg ?? `expected to ${
362
+            checkSend && checkReceive ? 'receive/send' : checkSend ? 'send' : 'receive'} data in 15s for ${this.name}`;
343 363
 
344
-        return driver.waitUntil(() => driver.execute(() => {
345
-            const stats = APP.conference.getStats();
364
+        return this.driver.waitUntil(() => this.driver.execute((pCheckSend: boolean, pCheckReceive: boolean) => {
365
+            const stats = APP?.conference?.getStats();
346 366
             const bitrateMap = stats?.bitrate || {};
347 367
             const rtpStats = {
348 368
                 uploadBitrate: bitrateMap.upload || 0,
349 369
                 downloadBitrate: bitrateMap.download || 0
350 370
             };
351 371
 
352
-            return rtpStats.uploadBitrate > 0 && rtpStats.downloadBitrate > 0;
353
-        }), {
372
+            return (rtpStats.uploadBitrate > 0 || !pCheckSend) && (rtpStats.downloadBitrate > 0 || !pCheckReceive);
373
+        }, checkSend, checkReceive), {
354 374
             timeout,
355
-            timeoutMsg: msg
356
-        });
357
-    }
358
-
359
-    /**
360
-     * Waits for send and receive data.
361
-     *
362
-     * @returns {Promise<void>}
363
-     */
364
-    async waitForSendData(
365
-            timeout = 15_000, msg = `expected to send data in 15s for ${this.name}`): Promise<void> {
366
-        const driver = this.driver;
367
-
368
-        return driver.waitUntil(() => driver.execute(() => {
369
-            const stats = APP.conference.getStats();
370
-            const bitrateMap = stats?.bitrate || {};
371
-            const rtpStats = {
372
-                uploadBitrate: bitrateMap.upload || 0
373
-            };
374
-
375
-            return rtpStats.uploadBitrate > 0;
376
-        }), {
377
-            timeout,
378
-            timeoutMsg: msg
375
+            timeoutMsg: lMsg
379 376
         });
380 377
     }
381 378
 
@@ -389,7 +386,7 @@ export class Participant {
389 386
         const driver = this.driver;
390 387
 
391 388
         return driver.waitUntil(() =>
392
-            driver.execute(count => APP.conference.getNumberOfParticipantsWithTracks() >= count, number), {
389
+            driver.execute(count => (APP?.conference?.getNumberOfParticipantsWithTracks() ?? -1) >= count, number), {
393 390
             timeout: 15_000,
394 391
             timeoutMsg: `expected number of remote streams:${number} in 15s for ${this.name}`
395 392
         });
@@ -405,10 +402,12 @@ export class Participant {
405 402
     waitForParticipants(number: number, msg?: string): Promise<void> {
406 403
         const driver = this.driver;
407 404
 
408
-        return driver.waitUntil(() => driver.execute(count => APP.conference.listMembers().length === count, number), {
409
-            timeout: 15_000,
410
-            timeoutMsg: msg || `not the expected participants ${number} in 15s for ${this.name}`
411
-        });
405
+        return driver.waitUntil(
406
+            () => driver.execute(count => (APP?.conference?.listMembers()?.length ?? -1) === count, number),
407
+            {
408
+                timeout: 15_000,
409
+                timeoutMsg: msg || `not the expected participants ${number} in 15s for ${this.name}`
410
+            });
412 411
     }
413 412
 
414 413
     /**
@@ -470,6 +469,15 @@ export class Participant {
470 469
         return new ParticipantsPane(this);
471 470
     }
472 471
 
472
+    /**
473
+     * Returns the large video page object.
474
+     *
475
+     * @returns {LargeVideo}
476
+     */
477
+    getLargeVideo(): LargeVideo {
478
+        return new LargeVideo(this);
479
+    }
480
+
473 481
     /**
474 482
      * Returns the videoQuality Dialog.
475 483
      *
@@ -546,7 +554,7 @@ export class Participant {
546 554
         }
547 555
 
548 556
         // do a hangup, to make sure unavailable presence is sent
549
-        await this.driver.execute(() => typeof APP !== 'undefined' && APP?.conference?.hangup());
557
+        await this.driver.execute(() => typeof APP !== 'undefined' && APP.conference?.hangup());
550 558
 
551 559
         // let's give it some time to leave the muc, we redirect after hangup so we should wait for the
552 560
         // change of url
@@ -608,29 +616,6 @@ export class Participant {
608 616
         return await avatar.isExisting() ? await avatar.getAttribute('src') : null;
609 617
     }
610 618
 
611
-    /**
612
-     * Gets avatar SRC attribute for the one displayed on large video.
613
-     */
614
-    async getLargeVideoAvatar() {
615
-        const avatar = this.driver.$('//img[@id="dominantSpeakerAvatar"]');
616
-
617
-        return await avatar.isExisting() ? await avatar.getAttribute('src') : null;
618
-    }
619
-
620
-    /**
621
-     * Returns resource part of the JID of the user who is currently displayed in the large video area.
622
-     */
623
-    async getLargeVideoResource() {
624
-        return await this.driver.execute(() => APP.UI.getLargeVideoID());
625
-    }
626
-
627
-    /**
628
-     * Returns the source of the large video currently shown.
629
-     */
630
-    async getLargeVideoId() {
631
-        return this.driver.execute('return document.getElementById("largeVideo").srcObject.id');
632
-    }
633
-
634 619
     /**
635 620
      * Makes sure that the avatar is displayed in the local thumbnail and that the video is not displayed.
636 621
      * There are 3 options for avatar:
@@ -700,6 +685,85 @@ export class Participant {
700 685
         return this.driver.$('div[data-testid="dialog.leaveReason"]').isDisplayed();
701 686
     }
702 687
 
688
+    /**
689
+     * Returns the audio level for a participant.
690
+     *
691
+     * @param observer
692
+     * @param participant
693
+     * @return
694
+     */
695
+    async getRemoteAudioLevel(p: Participant) {
696
+        const jid = await p.getEndpointId();
697
+
698
+        return await this.driver.execute(id => {
699
+            const level = APP?.conference?.getPeerSSRCAudioLevel(id);
700
+
701
+            return level ? level.toFixed(2) : null;
702
+        }, jid);
703
+    }
704
+
705
+    /**
706
+     * For the participant to have his audio muted/unmuted from given observer's
707
+     * perspective. The method will fail the test if something goes wrong or
708
+     * the audio muted status is different than the expected one. We wait up to
709
+     * 3 seconds for the expected status to appear.
710
+     *
711
+     * @param testee - instance of the participant for whom we're checking the audio muted status.
712
+     * @param muted - <tt>true</tt> to wait for audio muted status or <tt>false</tt> to wait for the participant to
713
+     * unmute.
714
+     */
715
+    async waitForAudioMuted(testee: Participant, muted: boolean): Promise<void> {
716
+        // Waits for the correct icon
717
+        await this.getFilmstrip().assertAudioMuteIconIsDisplayed(testee, !muted);
718
+
719
+        // Extended timeout for 'unmuted' to make tests more resilient to
720
+        // unexpected glitches.
721
+        const timeout = muted ? 3_000 : 6_000;
722
+
723
+        // Give it 3 seconds to not get any audio or to receive some
724
+        // depending on "muted" argument
725
+        try {
726
+            await this.driver.waitUntil(async () => {
727
+                const audioLevel = await this.getRemoteAudioLevel(testee);
728
+
729
+                if (muted) {
730
+                    if (audioLevel !== null && audioLevel > 0.1) {
731
+                        console.log(`muted exiting on: ${audioLevel}`);
732
+
733
+                        return true;
734
+                    }
735
+
736
+                    return false;
737
+                }
738
+
739
+                // When testing for unmuted we wait for first sound
740
+                if (audioLevel !== null && audioLevel > 0.1) {
741
+                    console.log(`unmuted exiting on: ${audioLevel}`);
742
+
743
+                    return true;
744
+                }
745
+
746
+                return false;
747
+            },
748
+            { timeout });
749
+
750
+            // When testing for muted we don't want to have
751
+            // the condition succeeded
752
+            if (muted) {
753
+                const name = await testee.displayName;
754
+
755
+                assert.fail(`There was some sound coming from muted: '${name}'`);
756
+            } // else we're good for unmuted participant
757
+        } catch (_timeoutE) {
758
+            if (!muted) {
759
+                const name = await testee.displayName;
760
+
761
+                assert.fail(`There was no sound from unmuted: '${name}'`);
762
+            } // else we're good for muted participant
763
+        }
764
+    }
765
+
766
+
703 767
     /**
704 768
      * Waits for remote video state - receiving and displayed.
705 769
      * @param endpointId

+ 32
- 0
tests/helpers/participants.ts Целия файл

@@ -53,6 +53,38 @@ export async function ensureThreeParticipants(ctx: IContext, options: IJoinOptio
53 53
     ]);
54 54
 }
55 55
 
56
+/**
57
+ * Creates the second participant instance or prepares one for re-joining.
58
+ *
59
+ * @param {Object} ctx - The context.
60
+ * @param {IJoinOptions} options - The options to use when joining the participant.
61
+ * @returns {Promise<void>}
62
+ */
63
+export function joinSecondParticipant(ctx: IContext, options: IJoinOptions = {}): Promise<void> {
64
+    return _joinParticipant('participant2', ctx.p2, p => {
65
+        ctx.p2 = p;
66
+    }, {
67
+        displayName: P2_DISPLAY_NAME,
68
+        ...options
69
+    });
70
+}
71
+
72
+/**
73
+ * Creates the third participant instance or prepares one for re-joining.
74
+ *
75
+ * @param {Object} ctx - The context.
76
+ * @param {IJoinOptions} options - The options to use when joining the participant.
77
+ * @returns {Promise<void>}
78
+ */
79
+export function joinThirdParticipant(ctx: IContext, options: IJoinOptions = {}): Promise<void> {
80
+    return _joinParticipant('participant3', ctx.p3, p => {
81
+        ctx.p3 = p;
82
+    }, {
83
+        displayName: P3_DISPLAY_NAME,
84
+        ...options
85
+    });
86
+}
87
+
56 88
 /**
57 89
  * Ensure that there are four participants.
58 90
  *

+ 1
- 1
tests/pageobjects/Filmstrip.ts Целия файл

@@ -36,7 +36,7 @@ export default class Filmstrip extends BasePageObject {
36 36
 
37 37
         await this.participant.driver.$(mutedIconXPath).waitForDisplayed({
38 38
             reverse,
39
-            timeout: 2000,
39
+            timeout: 5_000,
40 40
             timeoutMsg: `Audio mute icon is${reverse ? '' : ' not'} displayed for ${testee.name}`
41 41
         });
42 42
     }

+ 81
- 0
tests/pageobjects/LargeVideo.ts Целия файл

@@ -0,0 +1,81 @@
1
+import BasePageObject from './BasePageObject';
2
+
3
+/**
4
+ * The large video.
5
+ */
6
+export default class LargeVideo extends BasePageObject {
7
+    /**
8
+     * Returns the elapsed time at which video has been playing.
9
+     *
10
+     * @return {number} - The current play time of the video element.
11
+     */
12
+    async getPlaytime() {
13
+        return this.participant.driver.$('#largeVideo').getProperty('currentTime');
14
+    }
15
+
16
+    /**
17
+     * Waits 5s for the large video to switch to passed endpoint id.
18
+     *
19
+     * @param {string} endpointId - The endpoint.
20
+     * @returns {Promise<void>}
21
+     */
22
+    waitForSwitchTo(endpointId: string): Promise<void> {
23
+        return this.participant.driver.waitUntil(async () => endpointId === await this.getResource(), {
24
+            timeout: 5_000,
25
+            timeoutMsg: `expected large video to switch to ${endpointId} for 5s`
26
+        });
27
+    }
28
+
29
+    /**
30
+     * Gets avatar SRC attribute for the one displayed on large video.
31
+     */
32
+    async getAvatar() {
33
+        const avatar = this.participant.driver.$('//img[@id="dominantSpeakerAvatar"]');
34
+
35
+        return await avatar.isExisting() ? await avatar.getAttribute('src') : null;
36
+    }
37
+
38
+    /**
39
+     * Returns resource part of the JID of the user who is currently displayed in the large video area.
40
+     */
41
+    getResource() {
42
+        return this.participant.driver.execute(() => APP.UI.getLargeVideoID());
43
+    }
44
+
45
+    /**
46
+     * Returns the source of the large video currently shown.
47
+     */
48
+    getId() {
49
+        return this.participant.driver.execute('return document.getElementById("largeVideo").srcObject.id');
50
+    }
51
+
52
+
53
+    /**
54
+     * Checks if the large video is playing or not.
55
+     *
56
+     * @returns {Promise<void>}
57
+     */
58
+    assertPlaying() {
59
+        let lastTime: number;
60
+
61
+        return this.participant.driver.waitUntil(async () => {
62
+            const currentTime = parseFloat(await this.getPlaytime());
63
+
64
+            if (typeof lastTime === 'undefined') {
65
+                lastTime = currentTime;
66
+            }
67
+            if (currentTime > lastTime) {
68
+                return true;
69
+            }
70
+
71
+            lastTime = currentTime;
72
+
73
+            return false;
74
+        }, {
75
+            timeout: 5_500,
76
+            interval: 500,
77
+            timeoutMsg:
78
+                `Expected large video for participant ${this.participant.name} to play but it didn't for more than 5s`
79
+        });
80
+    }
81
+}

+ 24
- 0
tests/pageobjects/SettingsDialog.ts Целия файл

@@ -4,6 +4,8 @@ const EMAIL_FIELD = '#setEmail';
4 4
 const FOLLOW_ME_CHECKBOX = '//input[@name="follow-me"]';
5 5
 const HIDE_SELF_VIEW_CHECKBOX = '//input[@name="hide-self-view"]';
6 6
 const SETTINGS_DIALOG_CONTENT = '.settings-pane';
7
+const START_AUDIO_MUTED_CHECKBOX = '//input[@name="start-audio-muted"]';
8
+const START_VIDEO_MUTED_CHECKBOX = '//input[@name="start-video-muted"]';
7 9
 const X_PATH_MODERATOR_TAB = '//div[contains(@class, "settings-dialog")]//*[text()="Moderator"]';
8 10
 const X_PATH_MORE_TAB = '//div[contains(@class, "settings-dialog")]//*[text()="General"]';
9 11
 const X_PATH_PROFILE_TAB = '//div[contains(@class, "settings-dialog")]//*[text()="Profile"]';
@@ -78,6 +80,28 @@ export default class SettingsDialog extends BaseDialog {
78 80
         return this.clickOkButton();
79 81
     }
80 82
 
83
+    /**
84
+     * Sets the start audio muted feature to enabled/disabled.
85
+     * @param {boolean} enable - true for enabled and false for disabled.
86
+     * @returns {Promise<void>}
87
+     */
88
+    async setStartAudioMuted(enable: boolean) {
89
+        await this.openModeratorTab();
90
+
91
+        await this.setCheckbox(START_AUDIO_MUTED_CHECKBOX, enable);
92
+    }
93
+
94
+    /**
95
+     * Sets the start video muted feature to enabled/disabled.
96
+     * @param {boolean} enable - true for enabled and false for disabled.
97
+     * @returns {Promise<void>}
98
+     */
99
+    async setStartVideoMuted(enable: boolean) {
100
+        await this.openModeratorTab();
101
+
102
+        await this.setCheckbox(START_VIDEO_MUTED_CHECKBOX, enable);
103
+    }
104
+
81 105
     /**
82 106
      * Sets the state checked/selected of a checkbox in the settings dialog.
83 107
      */

+ 1
- 2
tests/specs/3way/activeSpeaker.spec.ts Целия файл

@@ -1,4 +1,3 @@
1
-/* global APP */
2 1
 import type { Participant } from '../../helpers/Participant';
3 2
 import { ensureThreeParticipants, muteAudioAndCheck } from '../../helpers/participants';
4 3
 
@@ -61,7 +60,7 @@ async function testActiveSpeaker(
61 60
     const otherParticipant1Driver = otherParticipant1.driver;
62 61
 
63 62
     await otherParticipant1Driver.waitUntil(
64
-        () => otherParticipant1Driver.execute(id => APP.UI.getLargeVideoID() === id, speakerEndpoint),
63
+        async () => await otherParticipant1.getLargeVideo().getResource() === speakerEndpoint,
65 64
         {
66 65
             timeout: 30_000, // 30 seconds
67 66
             timeoutMsg: 'Active speaker not displayed on large video.'

+ 6
- 6
tests/specs/3way/avatars.spec.ts Целия файл

@@ -50,7 +50,7 @@ describe('Avatar', () => {
50 50
             });
51 51
 
52 52
         // check if the avatar in the large video has changed
53
-        expect(await p2.getLargeVideoAvatar()).toContain(HASH);
53
+        expect(await p2.getLargeVideo().getAvatar()).toContain(HASH);
54 54
 
55 55
         // we check whether the default avatar of participant2 is displayed on both sides
56 56
         await p1.assertDefaultAvatarExist(p2);
@@ -72,12 +72,12 @@ describe('Avatar', () => {
72 72
         await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
73 73
 
74 74
         await p1.driver.waitUntil(
75
-            async () => (await p1.getLargeVideoAvatar())?.includes(HASH), {
75
+            async () => (await p1.getLargeVideo().getAvatar())?.includes(HASH), {
76 76
                 timeout: 2000,
77 77
                 timeoutMsg: 'Avatar on large video did not change'
78 78
             });
79 79
 
80
-        const p1LargeSrc = await p1.getLargeVideoAvatar();
80
+        const p1LargeSrc = await p1.getLargeVideo().getAvatar();
81 81
         const p1ThumbSrc = await p1.getLocalVideoAvatar();
82 82
 
83 83
         // Check if avatar on large video is the same as on local thumbnail
@@ -96,7 +96,7 @@ describe('Avatar', () => {
96 96
 
97 97
         // Check if p1's avatar is on large video now
98 98
         await p2.driver.waitUntil(
99
-              async () => await p2.getLargeVideoAvatar() === p1LargeSrc, {
99
+              async () => await p2.getLargeVideo().getAvatar() === p1LargeSrc, {
100 100
                   timeout: 2000,
101 101
                   timeoutMsg: 'Avatar on large video did not change'
102 102
               });
@@ -141,7 +141,7 @@ describe('Avatar', () => {
141 141
 
142 142
         // The avatar should be on large video and display name instead of an avatar, local video displayed
143 143
         await p3.driver.waitUntil(
144
-            async () => await p3.getLargeVideoResource() === p1EndpointId, {
144
+            async () => await p3.getLargeVideo().getResource() === p1EndpointId, {
145 145
                 timeout: 2000,
146 146
                 timeoutMsg: `Large video did not switch to ${p1.name}`
147 147
             });
@@ -158,7 +158,7 @@ describe('Avatar', () => {
158 158
 
159 159
         // The avatar should be on large video and display name instead of an avatar, local video displayed
160 160
         await p3.driver.waitUntil(
161
-            async () => await p3.getLargeVideoResource() === p2EndpointId, {
161
+            async () => await p3.getLargeVideo().getResource() === p2EndpointId, {
162 162
                 timeout: 2000,
163 163
                 timeoutMsg: `Large video did not switch to ${p2.name}`
164 164
             });

+ 2
- 2
tests/specs/3way/followMe.spec.ts Целия файл

@@ -75,7 +75,7 @@ describe('Follow Me', () => {
75 75
         const localVideoId = await p2Filmstrip.getLocalVideoId();
76 76
 
77 77
         await p2.driver.waitUntil(
78
-            async () => await localVideoId === await p2.getLargeVideoId(),
78
+            async () => await localVideoId === await p2.getLargeVideo().getId(),
79 79
             {
80 80
                 timeout: 5_000,
81 81
                 timeoutMsg: 'The pinned participant is not displayed on stage for p2'
@@ -84,7 +84,7 @@ describe('Follow Me', () => {
84 84
         const p2VideoIdOnp3 = await p3.getFilmstrip().getRemoteVideoId(await p2.getEndpointId());
85 85
 
86 86
         await p3.driver.waitUntil(
87
-            async () => p2VideoIdOnp3 === await p3.getLargeVideoId(),
87
+            async () => p2VideoIdOnp3 === await p3.getLargeVideo().getId(),
88 88
             {
89 89
                 timeout: 5_000,
90 90
                 timeoutMsg: 'The pinned participant is not displayed on stage for p3'

+ 265
- 0
tests/specs/3way/startMuted.spec.ts Целия файл

@@ -0,0 +1,265 @@
1
+import {
2
+    ensureOneParticipant,
3
+    ensureTwoParticipants,
4
+    joinSecondParticipant,
5
+    joinThirdParticipant,
6
+    unmuteVideoAndCheck
7
+} from '../../helpers/participants';
8
+
9
+describe('StartMuted', () => {
10
+    it('checkboxes test', async () => {
11
+        const options = { configOverwrite: { p2p: { enabled: true } } };
12
+
13
+        await ensureOneParticipant(ctx, options);
14
+
15
+        const { p1 } = ctx;
16
+
17
+        await p1.getToolbar().clickSettingsButton();
18
+
19
+        const settingsDialog = p1.getSettingsDialog();
20
+
21
+        await settingsDialog.waitForDisplay();
22
+
23
+        await settingsDialog.setStartAudioMuted(true);
24
+        await settingsDialog.setStartVideoMuted(true);
25
+        await settingsDialog.submit();
26
+
27
+        await joinSecondParticipant(ctx, {
28
+            ...options,
29
+            skipInMeetingChecks: true
30
+        });
31
+
32
+        const { p2 } = ctx;
33
+
34
+        await p2.waitForIceConnected();
35
+        await p2.waitForSendReceiveData({ checkSend: false });
36
+
37
+        await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p2);
38
+        await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
39
+        await p1.waitForAudioMuted(p2, true);
40
+
41
+
42
+        await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p1, true);
43
+        await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1, true);
44
+
45
+        // Enable video on p2 and check if p2 appears unmuted on p1.
46
+        await Promise.all([
47
+            p2.getToolbar().clickAudioUnmuteButton(), p2.getToolbar().clickVideoUnmuteButton()
48
+        ]);
49
+
50
+        await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p2, true);
51
+        await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
52
+
53
+        await p1.waitForAudioMuted(p2, false);
54
+
55
+        // Add a third participant and check p3 is able to receive audio and video from p2.
56
+        await joinThirdParticipant(ctx, {
57
+            ...options,
58
+            skipInMeetingChecks: true
59
+        });
60
+
61
+        const { p3 } = ctx;
62
+
63
+        await p3.waitForIceConnected();
64
+        await p3.waitForSendReceiveData({ checkSend: false });
65
+
66
+        await p3.getFilmstrip().assertAudioMuteIconIsDisplayed(p2, true);
67
+        await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
68
+    });
69
+
70
+
71
+    it('config options test', async () => {
72
+        await hangupAllParticipants();
73
+
74
+        const options = {
75
+            configOverwrite: {
76
+                testing: {
77
+                    testMode: true,
78
+                    debugAudioLevels: true
79
+                },
80
+                startAudioMuted: 2,
81
+                startVideoMuted: 2
82
+            }
83
+        };
84
+
85
+        await ensureOneParticipant(ctx, options);
86
+        await joinSecondParticipant(ctx, { skipInMeetingChecks: true });
87
+
88
+        const { p2 } = ctx;
89
+
90
+        await p2.waitForIceConnected();
91
+        await p2.waitForSendReceiveData({ checkSend: false });
92
+
93
+        await joinThirdParticipant(ctx, { skipInMeetingChecks: true });
94
+
95
+        const { p3 } = ctx;
96
+
97
+        await p3.waitForIceConnected();
98
+        await p3.waitForSendReceiveData({ checkSend: false });
99
+
100
+        const { p1 } = ctx;
101
+
102
+        const p2ID = await p2.getEndpointId();
103
+
104
+
105
+        p1.log(`Start configOptionsTest, second participant: ${p2ID}`);
106
+
107
+
108
+        // Participant 3 should be muted, 1 and 2 unmuted.
109
+        await p3.getFilmstrip().assertAudioMuteIconIsDisplayed(p3);
110
+        await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p3);
111
+
112
+        await Promise.all([
113
+            p1.waitForAudioMuted(p3, true),
114
+            p2.waitForAudioMuted(p3, true)
115
+        ]);
116
+
117
+        await p3.getFilmstrip().assertAudioMuteIconIsDisplayed(p1, true);
118
+        await p3.getFilmstrip().assertAudioMuteIconIsDisplayed(p2, true);
119
+        await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1, true);
120
+        await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
121
+
122
+        // Unmute and see if the audio works
123
+        await p3.getToolbar().clickAudioUnmuteButton();
124
+        p1.log('configOptionsTest, unmuted third participant');
125
+        await p1.waitForAudioMuted(p3, false /* unmuted */);
126
+    });
127
+
128
+    it('startWithVideoMuted=true can unmute', async () => {
129
+        // Maybe disable if there is FF or Safari participant.
130
+
131
+        await hangupAllParticipants();
132
+
133
+        // Explicitly enable P2P due to a regression with unmute not updating
134
+        // large video while in P2P.
135
+        const options = {
136
+            configOverwrite: {
137
+                p2p: {
138
+                    enabled: true
139
+                },
140
+                startWithVideoMuted: true
141
+            }
142
+        };
143
+
144
+        await ensureTwoParticipants(ctx, options);
145
+
146
+        const { p1, p2 } = ctx;
147
+
148
+
149
+        await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
150
+        await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
151
+
152
+        await Promise.all([
153
+            p1.getLargeVideo().waitForSwitchTo(await p2.getEndpointId()),
154
+            p2.getLargeVideo().waitForSwitchTo(await p1.getEndpointId())
155
+        ]);
156
+
157
+        await unmuteVideoAndCheck(p2, p1);
158
+
159
+        await p1.getLargeVideo().assertPlaying();
160
+    });
161
+
162
+    it('startWithAudioMuted=true can unmute', async () => {
163
+        await hangupAllParticipants();
164
+
165
+        const options = {
166
+            configOverwrite: {
167
+                startWithAudioMuted: true
168
+            }
169
+        };
170
+
171
+        await ensureTwoParticipants(ctx, options);
172
+
173
+        const { p1, p2 } = ctx;
174
+
175
+        await Promise.all([ p1.waitForAudioMuted(p2, true), p2.waitForAudioMuted(p1, true) ]);
176
+        await p1.getToolbar().clickAudioUnmuteButton();
177
+        await Promise.all([ p1.waitForAudioMuted(p2, true), p2.waitForAudioMuted(p1, false) ]);
178
+    });
179
+
180
+    it('startWithAudioVideoMuted=true can unmute', async () => {
181
+        await hangupAllParticipants();
182
+
183
+        const options = {
184
+            configOverwrite: {
185
+                startWithAudioMuted: true,
186
+                startWithVideoMuted: true,
187
+                p2p: {
188
+                    enabled: true
189
+                }
190
+            }
191
+        };
192
+
193
+        await Promise.all([
194
+            ensureOneParticipant(ctx, options),
195
+            joinSecondParticipant(ctx, {
196
+                configOverwrite: {
197
+                    p2p: {
198
+                        enabled: true
199
+                    }
200
+                },
201
+                skipInMeetingChecks: true
202
+            })
203
+        ]);
204
+
205
+        const { p1, p2 } = ctx;
206
+
207
+        await p2.waitForIceConnected();
208
+        await p2.waitForSendReceiveData({ checkReceive: false });
209
+
210
+        await p2.waitForAudioMuted(p1, true);
211
+        await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
212
+
213
+        // Unmute p1's both audio and video and check on p2.
214
+        await p1.getToolbar().clickAudioUnmuteButton();
215
+        await p2.waitForAudioMuted(p1, false);
216
+
217
+        await unmuteVideoAndCheck(p1, p2);
218
+        await p2.getLargeVideo().assertPlaying();
219
+    });
220
+
221
+
222
+    it('test p2p JVB switch and switch back', async () => {
223
+        const { p1, p2 } = ctx;
224
+
225
+        // Mute p2's video just before p3 joins.
226
+        await p2.getToolbar().clickVideoMuteButton();
227
+
228
+        await joinThirdParticipant(ctx, {
229
+            configOverwrite: {
230
+                p2p: {
231
+                    enabled: true
232
+                }
233
+            }
234
+        });
235
+
236
+        const { p3 } = ctx;
237
+
238
+        // Unmute p2 and check if its video is being received by p1 and p3.
239
+        await p2.getToolbar().clickVideoUnmuteButton();
240
+
241
+        await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
242
+        await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
243
+
244
+        // Mute p2's video just before p3 leaves.
245
+        await p2.getToolbar().clickVideoMuteButton();
246
+
247
+        await p3.hangup();
248
+
249
+        await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
250
+
251
+        await p2.getToolbar().clickVideoUnmuteButton();
252
+
253
+        // Check if p2's video is playing on p1.
254
+        await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
255
+        await p1.getLargeVideo().assertPlaying();
256
+    });
257
+});
258
+
259
+/**
260
+ * Hangs up all participants (p1, p2 and p3)
261
+ * @returns {Promise<void>}
262
+ */
263
+function hangupAllParticipants() {
264
+    return Promise.all([ ctx.p1?.hangup(), ctx.p2?.hangup(), ctx.p3?.hangup() ]);
265
+}

+ 1
- 1
tests/specs/4way/desktopSharing.spec.ts Целия файл

@@ -251,7 +251,7 @@ describe('Desktop sharing', () => {
251 251
         await checkForScreensharingTile(p3, p2);
252 252
 
253 253
         // The desktop sharing participant should be on large
254
-        expect(await p1.driver.execute(() => APP.UI.getLargeVideoID())).toBe(`${await p3.getEndpointId()}-v1`);
254
+        expect(await p1.getLargeVideo().getResource()).toBe(`${await p3.getEndpointId()}-v1`);
255 255
 
256 256
         // the video should be playing
257 257
         await p1.driver.waitUntil(() => p1.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived()), {

+ 1
- 1
tests/specs/4way/lastN.spec.ts Целия файл

@@ -23,7 +23,7 @@ describe('lastN', () => {
23 23
         const { p3 } = ctx;
24 24
         const p3Toolbar = p3.getToolbar();
25 25
 
26
-        await p3.waitForSendData();
26
+        await p3.waitForSendReceiveData({ checkReceive: false });
27 27
 
28 28
         await ctx.p1.waitForRemoteVideo(await p3.getEndpointId());
29 29
 

Loading…
Отказ
Запис