浏览代码

feat(tests): Adds desktop sharing test.

factor2
damencho 5 个月前
父节点
当前提交
62d1ee606a
共有 5 个文件被更改,包括 378 次插入3 次删除
  1. 16
    0
      tests/pageobjects/Toolbar.ts
  2. 353
    0
      tests/specs/4way/desktopSharing.spec.ts
  3. 4
    1
      tests/wdio.conf.ts
  4. 4
    1
      tests/wdio.firefox.conf.ts
  5. 1
    1
      tests/wdio.grid.conf.ts

+ 16
- 0
tests/pageobjects/Toolbar.ts 查看文件

@@ -5,6 +5,7 @@ const AUDIO_UNMUTE = 'Unmute microphone';
5 5
 const CHAT = 'Open chat';
6 6
 const CLOSE_CHAT = 'Close chat';
7 7
 const CLOSE_PARTICIPANTS_PANE = 'Close participants pane';
8
+const DESKTOP = 'Start sharing your screen';
8 9
 const HANGUP = 'Leave the meeting';
9 10
 const OVERFLOW_MENU = 'More actions menu';
10 11
 const OVERFLOW = 'More actions';
@@ -12,6 +13,7 @@ const PARTICIPANTS = 'Open participants pane';
12 13
 const PROFILE = 'Edit your profile';
13 14
 const RAISE_HAND = 'Raise your hand';
14 15
 const SETTINGS = 'Open settings';
16
+const STOP_DESKTOP = 'Stop sharing your screen';
15 17
 const ENTER_TILE_VIEW_BUTTON = 'Enter tile view';
16 18
 const EXIT_TILE_VIEW_BUTTON = 'Exit tile view';
17 19
 const VIDEO_QUALITY = 'Manage video quality';
@@ -164,6 +166,20 @@ export default class Toolbar extends BasePageObject {
164 166
         await this.getButton(CLOSE_CHAT).click();
165 167
     }
166 168
 
169
+    /**
170
+     * Clicks on the desktop sharing button that starts desktop sharing.
171
+     */
172
+    async clickDesktopSharingButton() {
173
+        await this.getButton(DESKTOP).click();
174
+    }
175
+
176
+    /**
177
+     * Clicks on the desktop sharing button to stop it.
178
+     */
179
+    async clickStopDesktopSharingButton() {
180
+        await this.getButton(STOP_DESKTOP).click();
181
+    }
182
+
167 183
     /**
168 184
      * Clicks on the tile view button which enables tile layout.
169 185
      */

+ 353
- 0
tests/specs/4way/desktopSharing.spec.ts 查看文件

@@ -0,0 +1,353 @@
1
+import { SET_AUDIO_ONLY } from '../../../react/features/base/audio-only/actionTypes';
2
+import type { Participant } from '../../helpers/Participant';
3
+import {
4
+    ensureFourParticipants,
5
+    ensureOneParticipant,
6
+    ensureThreeParticipants,
7
+    ensureTwoParticipants
8
+} from '../../helpers/participants';
9
+
10
+describe('Desktop sharing - ', () => {
11
+    it('start', async () => {
12
+        await ensureTwoParticipants(ctx, {
13
+            configOverwrite: {
14
+                p2p: {
15
+                    enabled: true
16
+                }
17
+            }
18
+        });
19
+        const { p1, p2 } = ctx;
20
+
21
+        await p2.getToolbar().clickDesktopSharingButton();
22
+
23
+        // Check if a remote screen share tile is created on p1.
24
+        await checkForScreensharingTile(p2, p1);
25
+
26
+        // Check if a local screen share tile is created on p2.
27
+        await checkForScreensharingTile(p2, p2);
28
+
29
+        expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
30
+    });
31
+
32
+    it('stop', async () => {
33
+        const { p1, p2 } = ctx;
34
+
35
+        await p2.getToolbar().clickStopDesktopSharingButton();
36
+
37
+        // Check if the local screen share thumbnail disappears on p2.
38
+        await checkForScreensharingTile(p2, p2, true);
39
+
40
+        // Check if the remote screen share thumbnail disappears on p1.
41
+        await checkForScreensharingTile(p1, p2, true);
42
+    });
43
+
44
+    /**
45
+     * Ensures screen share is still visible when the call switches from p2p to jvb connection.
46
+     */
47
+    it('p2p to jvb switch', async () => {
48
+        await ctx.p2.getToolbar().clickDesktopSharingButton();
49
+
50
+        await ensureThreeParticipants(ctx);
51
+        const { p1, p2, p3 } = ctx;
52
+
53
+        // Check if a remote screen share tile is created on all participants.
54
+        await checkForScreensharingTile(p2, p1);
55
+        await checkForScreensharingTile(p2, p2);
56
+        await checkForScreensharingTile(p2, p2);
57
+
58
+        expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
59
+    });
60
+
61
+    /**
62
+     * Ensure screen share is still visible when the call switches from jvb to p2p and back.
63
+     */
64
+    it('p2p to jvb switch and back', async () => {
65
+        const { p1, p2, p3 } = ctx;
66
+
67
+        await p3.hangup();
68
+
69
+        // Check if a remote screen share tile is created on p1 and p2 after switching back to p2p.
70
+        await checkForScreensharingTile(p2, p1);
71
+        await checkForScreensharingTile(p2, p2);
72
+
73
+        // The video should be playing.
74
+        expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
75
+
76
+        // Start desktop share on p1.
77
+        await p1.getToolbar().clickDesktopSharingButton();
78
+
79
+        // Check if a new tile for p1's screen share is created on both p1 and p2.
80
+        await checkForScreensharingTile(p1, p1);
81
+        await checkForScreensharingTile(p1, p2);
82
+
83
+        await ensureThreeParticipants(ctx);
84
+
85
+        await checkForScreensharingTile(p1, p3);
86
+        await checkForScreensharingTile(p2, p3);
87
+
88
+        // The large video should be playing on p3.
89
+        expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
90
+    });
91
+
92
+    /**
93
+     * Ensure that screen share is still visible in jvb connection when share is toggled while the users are
94
+     * in p2p mode, i.e., share is restarted when user is in p2p mode and then the call switches over to jvb mode.
95
+     */
96
+    it('stop screen sharing and back', async () => {
97
+        const { p1, p2, p3 } = ctx;
98
+
99
+        // Stop share on both p1 and p2.
100
+        await p1.getToolbar().clickStopDesktopSharingButton();
101
+        await p2.getToolbar().clickStopDesktopSharingButton();
102
+
103
+        await p3.hangup();
104
+
105
+        // Start share on both p1 and p2.
106
+        await p1.getToolbar().clickDesktopSharingButton();
107
+        await p2.getToolbar().clickDesktopSharingButton();
108
+
109
+        // Check if p1 and p2 can see each other's shares in p2p.
110
+        await checkForScreensharingTile(p1, p2);
111
+        await checkForScreensharingTile(p2, p1);
112
+
113
+        // Add p3 back to the conference and check if p1 and p2's shares are visible on p3.
114
+        await ensureThreeParticipants(ctx);
115
+
116
+        await checkForScreensharingTile(p1, p3);
117
+        await checkForScreensharingTile(p2, p3);
118
+
119
+        // The large video should be playing on p3.
120
+        expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
121
+    });
122
+
123
+    /**
124
+     *  Ensures screen share is visible when a muted screen share track is added to the conference, i.e.,
125
+     *  users starts and stops the share before anyone else joins the call.
126
+     *  The call switches to jvb and then back to p2p.
127
+     */
128
+    it('screen sharing toggle before others join', async () => {
129
+        await Promise.all([ ctx.p1.hangup(), ctx.p2.hangup(), ctx.p3.hangup() ]);
130
+
131
+        await ensureOneParticipant(ctx, {
132
+            configOverwrite: {
133
+                p2p: {
134
+                    enabled: true
135
+                }
136
+            }
137
+        });
138
+        const { p1 } = ctx;
139
+
140
+        // p1 starts share when alone in the call.
141
+        await p1.getToolbar().clickDesktopSharingButton();
142
+        await checkForScreensharingTile(p1, p1);
143
+
144
+        // p1 stops share.
145
+        await p1.getToolbar().clickStopDesktopSharingButton();
146
+
147
+        // Call switches to jvb.
148
+        await ensureThreeParticipants(ctx);
149
+        const { p2, p3 } = ctx;
150
+
151
+        // p1 starts share again when call switches to jvb.
152
+        await p1.getToolbar().clickDesktopSharingButton();
153
+
154
+        // Check p2 and p3 are able to see p1's share.
155
+        await checkForScreensharingTile(p1, p2);
156
+        await checkForScreensharingTile(p1, p3);
157
+
158
+        expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
159
+        expect(await p3.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
160
+
161
+        // p3 leaves the call.
162
+        await p3.hangup();
163
+
164
+        // Make sure p2 see's p1's share after the call switches back to p2p.
165
+        await checkForScreensharingTile(p1, p2);
166
+        expect(await p2.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
167
+
168
+        // p2 starts share when in p2p.
169
+        await p2.getToolbar().clickDesktopSharingButton();
170
+
171
+        // Makes sure p2's share is visible on p1.
172
+        await checkForScreensharingTile(p2, p1);
173
+        expect(await p1.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
174
+    });
175
+
176
+    /**
177
+     * A case where a non-dominant speaker is sharing screen for a participant in low bandwidth mode
178
+     * where only a screen share can be received. A bug fixed in jvb 0c5dd91b where the video was not received.
179
+     */
180
+    it('audio only and non dominant screen share', async () => {
181
+        await Promise.all([ ctx.p1.hangup(), ctx.p2.hangup(), ctx.p3.hangup() ]);
182
+
183
+        await ensureOneParticipant(ctx);
184
+        const { p1 } = ctx;
185
+
186
+        // a workaround to directly set audio only mode without going through the rest of the settings in the UI
187
+        await p1.driver.execute(type => {
188
+            APP.store.dispatch({
189
+                type,
190
+                audioOnly: true
191
+            });
192
+            APP.conference.onToggleAudioOnly();
193
+        }, SET_AUDIO_ONLY);
194
+        await p1.getToolbar().clickAudioMuteButton();
195
+
196
+        await ensureThreeParticipants(ctx, { skipInMeetingChecks: true });
197
+        const { p2, p3 } = ctx;
198
+
199
+        await p3.getToolbar().clickAudioMuteButton();
200
+        await p3.getToolbar().clickDesktopSharingButton();
201
+
202
+        await checkForScreensharingTile(p3, p1);
203
+        await checkForScreensharingTile(p3, p2);
204
+
205
+        // the video should be playing
206
+        await p1.driver.waitUntil(async () =>
207
+            await p1.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived()), {
208
+            timeout: 5_000,
209
+            timeoutMsg: 'expected remote screen share to be on large'
210
+        });
211
+    });
212
+
213
+    /**
214
+     * A case where first participant is muted (a&v) and enters low bandwidth mode,
215
+     * the second one is audio muted only and the one sharing (the third) is dominant speaker.
216
+     * A problem fixed in jitsi-meet 3657c19e and d6ab0a72.
217
+     */
218
+    it('audio only and dominant screen share', async () => {
219
+        await Promise.all([ ctx.p1.hangup(), ctx.p2.hangup(), ctx.p3.hangup() ]);
220
+
221
+        await ensureOneParticipant(ctx, {
222
+            configOverwrite: {
223
+                startWithAudioMuted: true,
224
+                startWithVideoMuted: true
225
+            }
226
+        });
227
+        const { p1 } = ctx;
228
+
229
+        // a workaround to directly set audio only mode without going through the rest of the settings in the UI
230
+        await p1.driver.execute(type => {
231
+            APP.store.dispatch({
232
+                type,
233
+                audioOnly: true
234
+            });
235
+            APP.conference.onToggleAudioOnly();
236
+        }, SET_AUDIO_ONLY);
237
+
238
+        await ensureTwoParticipants(ctx, {
239
+            skipInMeetingChecks: true,
240
+            configOverwrite: {
241
+                startWithAudioMuted: true
242
+            }
243
+        });
244
+        await ensureThreeParticipants(ctx, {
245
+            skipInMeetingChecks: true
246
+        });
247
+        const { p2, p3 } = ctx;
248
+
249
+        await p3.getToolbar().clickDesktopSharingButton();
250
+
251
+        await checkForScreensharingTile(p3, p1);
252
+        await checkForScreensharingTile(p3, p2);
253
+
254
+        // The desktop sharing participant should be on large
255
+        expect(await p1.driver.execute(() => APP.UI.getLargeVideoID())).toBe(`${await p3.getEndpointId()}-v1`);
256
+
257
+        // the video should be playing
258
+        await p1.driver.waitUntil(async () =>
259
+            await p1.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived()), {
260
+            timeout: 5_000,
261
+            timeoutMsg: 'expected remote screen share to be on large'
262
+        });
263
+    });
264
+
265
+    /**
266
+     * Test screensharing with lastN. We add p4 with lastN=2 and verify that it receives the expected streams.
267
+     */
268
+    it('with lastN', async () => {
269
+        await Promise.all([ ctx.p1.hangup(), ctx.p2.hangup(), ctx.p3.hangup() ]);
270
+
271
+        await ensureThreeParticipants(ctx);
272
+        const { p1, p2, p3 } = ctx;
273
+
274
+        await p3.getToolbar().clickDesktopSharingButton();
275
+
276
+        await p1.getToolbar().clickAudioMuteButton();
277
+        await p3.getToolbar().clickAudioMuteButton();
278
+
279
+        await ensureFourParticipants(ctx, {
280
+            configOverwrite: {
281
+                channelLastN: 2,
282
+                startWithAudioMuted: true
283
+            }
284
+        });
285
+        const { p4 } = ctx;
286
+
287
+        // We now have p1, p2, p3, p4.
288
+        // p3 is screensharing.
289
+        // p1, p3, p4 are audio muted, so p2 should eventually become dominant speaker.
290
+        // Participants should display p3 on-stage because it is screensharing.
291
+        await checkForScreensharingTile(p3, p1);
292
+        await checkForScreensharingTile(p3, p2);
293
+        await checkForScreensharingTile(p3, p4);
294
+
295
+        // And the video should be playing
296
+        expect(await p4.driver.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
297
+
298
+        const p1EndpointId = await p1.getEndpointId();
299
+
300
+        // p4 has lastN=2 and has selected p3. With p2 being dominant speaker p4 should eventually
301
+        // see video for [p3, p2] and p1 as ninja.
302
+        await p4.driver.$(`//span[@id='participant_${p1EndpointId}']//span[@class='connection_ninja']`)
303
+            .waitForDisplayed({
304
+                timeout: 15_000
305
+            });
306
+
307
+        const p2EndpointId = await p2.getEndpointId();
308
+
309
+        await p4.driver.waitUntil(async () =>
310
+            await p4.driver.execute(epId => JitsiMeetJS.app.testing.isRemoteVideoReceived(`${epId}`),
311
+                p2EndpointId) && await p4.driver.$(
312
+                `//span[@id="participant_${p2EndpointId}" and contains(@class, "display-video")]`).isExisting(), {
313
+            timeout: 15_000,
314
+            timeoutMsg: 'expected remote video to be received 15s'
315
+        });
316
+
317
+        await p4.driver.waitUntil(async () =>
318
+            await p4.driver.execute(epId => JitsiMeetJS.app.testing.isRemoteVideoReceived(`${epId}`),
319
+                p2EndpointId) && await p4.driver.$(
320
+                `//span[@id="participant_${p2EndpointId}" and contains(@class, "display-video")]`).isExisting(), {
321
+            timeout: 15_000,
322
+            timeoutMsg: 'expected remote video to be received 15s'
323
+        });
324
+
325
+        // Let's switch and check, muting participant 2 and unmuting 1 will leave participant 1 as dominant
326
+        await p1.getToolbar().clickAudioUnmuteButton();
327
+        await p2.getToolbar().clickAudioMuteButton();
328
+
329
+        // Participant4 should eventually see video for [p3, p1] and p2 as a ninja.
330
+        await p4.driver.$(`//span[@id='participant_${p2EndpointId}']//span[@class='connection_ninja']`)
331
+            .waitForDisplayed({
332
+                timeout: 15_000
333
+            });
334
+
335
+        await p4.driver.waitUntil(async () =>
336
+            await p4.driver.execute(epId => JitsiMeetJS.app.testing.isRemoteVideoReceived(`${epId}`),
337
+                p1EndpointId) && await p4.driver.$(
338
+              `//span[@id="participant_${p1EndpointId}" and contains(@class, "display-video")]`).isExisting(), {
339
+            timeout: 15_000,
340
+            timeoutMsg: 'expected remote video to be received 15s'
341
+        });
342
+    });
343
+});
344
+
345
+/**
346
+ * Check if a screensharing tile is displayed on the observer.
347
+ */
348
+async function checkForScreensharingTile(sharer: Participant, observer: Participant, reverse = false) {
349
+    await observer.driver.$(`//span[@id='participant_${await sharer.getEndpointId()}-v1']`).waitForDisplayed({
350
+        timeout: 3_000,
351
+        reverse
352
+    });
353
+}

+ 4
- 1
tests/wdio.conf.ts 查看文件

@@ -17,6 +17,8 @@ const allure = require('allure-commandline');
17 17
 // we need it to be able to reuse jitsi-meet code in tests
18 18
 require.extensions['.web.ts'] = require.extensions['.ts'];
19 19
 
20
+const usingGrid = Boolean(new URL(import.meta.url).searchParams.get('grid'));
21
+
20 22
 const chromeArgs = [
21 23
     '--allow-insecure-localhost',
22 24
     '--use-fake-ui-for-media-stream',
@@ -33,7 +35,8 @@ const chromeArgs = [
33 35
     // Avoids - "You are checking for animations on an inactive tab, animations do not run for inactive tabs"
34 36
     // when executing waitForStable()
35 37
     '--disable-renderer-backgrounding',
36
-    `--use-file-for-fake-audio-capture=${process.env.REMOTE_RESOURCE_PATH || 'tests/resources'}/fakeAudioStream.wav`
38
+    `--use-file-for-fake-audio-capture=${
39
+        usingGrid ? process.env.REMOTE_RESOURCE_PATH : 'tests/resources'}/fakeAudioStream.wav`
37 40
 ];
38 41
 
39 42
 if (process.env.RESOLVER_RULES) {

+ 4
- 1
tests/wdio.firefox.conf.ts 查看文件

@@ -21,7 +21,10 @@ if (process.env.HEADLESS === 'true') {
21 21
 
22 22
 const ffExcludes = [
23 23
     'specs/2way/iFrameParticipantsPresence.spec.ts', // FF does not support uploading files (uploadFile)
24
-    'specs/3way/activeSpeaker.spec.ts' // FF does not support setting a file as mic input
24
+
25
+    // FF does not support setting a file as mic input, no dominant speaker events
26
+    'specs/3way/activeSpeaker.spec.ts',
27
+    'specs/4way/desktopSharing.spec.ts'
25 28
 ];
26 29
 
27 30
 const mergedConfig = merge(defaultConfig, {

+ 1
- 1
tests/wdio.grid.conf.ts 查看文件

@@ -4,7 +4,7 @@ import { merge } from 'lodash-es';
4 4
 import { URL } from 'url';
5 5
 
6 6
 // @ts-ignore
7
-import { config as defaultConfig } from './wdio.conf.ts';
7
+import { config as defaultConfig } from './wdio.conf.ts?grid=true';
8 8
 
9 9
 const gridUrl = new URL(process.env.GRID_HOST_URL as string);
10 10
 const protocol = gridUrl.protocol.replace(':', '');

正在加载...
取消
保存