Browse Source

feat(tests): Recording and live-streaming tests.

j25
damencho 5 months ago
parent
commit
403b9043b6

+ 4
- 0
tests/env.example View File

55
 # Whether to use beta for the first participants
55
 # Whether to use beta for the first participants
56
 #BROWSER_CHROME_BETA=false
56
 #BROWSER_CHROME_BETA=false
57
 #BROWSER_FF_BETA=false
57
 #BROWSER_FF_BETA=false
58
+
59
+# A stream key abd broadcast ID that can be used by the tests to stream to YouTube
60
+#YTUBE_TEST_STREAM_KEY=
61
+#YTUBE_TEST_BROADCAST_ID=

+ 2
- 1
tests/helpers/participants.ts View File

319
                 'outbound-call': 'true',
319
                 'outbound-call': 'true',
320
                 'transcription': 'true',
320
                 'transcription': 'true',
321
                 'recording': 'true',
321
                 'recording': 'true',
322
-                'sip-outbound-call': true
322
+                'sip-outbound-call': true,
323
+                'livestreaming': true
323
             },
324
             },
324
         },
325
         },
325
         'room': '*'
326
         'room': '*'

+ 23
- 0
tests/pageobjects/IframeAPI.ts View File

109
             address: v
109
             address: v
110
         } ]), value);
110
         } ]), value);
111
     }
111
     }
112
+
113
+    /**
114
+     * Starts a file recording or streaming session.
115
+     * @param options
116
+     */
117
+    startRecording(options: any) {
118
+        return this.participant.execute(o => window.jitsiAPI.startRecording(o), options);
119
+    }
120
+
121
+    /**
122
+     * Stop a file recording or streaming session.
123
+     * @param mode
124
+     */
125
+    stopRecording(mode: string) {
126
+        return this.participant.execute(m => window.jitsiAPI.stopRecording(m), mode);
127
+    }
128
+
129
+    /**
130
+     * Returns the live-streaming url.
131
+     */
132
+    async getLivestreamUrl() {
133
+        return this.participant.execute(() => window.jitsiAPI.getLivestreamUrl());
134
+    }
112
 }
135
 }

+ 193
- 0
tests/specs/alone/iFrameApiRecording.spec.ts View File

1
+import { ensureOneParticipant } from '../../helpers/participants';
2
+
3
+describe('Recording', () => {
4
+    it('join participant', async () => {
5
+        await ensureOneParticipant(ctx);
6
+
7
+        const { p1 } = ctx;
8
+
9
+        // check for dial-in dial-out sip-jibri maybe
10
+        if (await p1.execute(() => config.disableIframeAPI)) {
11
+            // skip the test if iframeAPI is disabled
12
+            ctx.skipSuiteTests = true;
13
+
14
+            return;
15
+        }
16
+
17
+        ctx.data.recordingDisabled = Boolean(!await p1.execute(() => config.recordingService?.enabled));
18
+        ctx.data.liveStreamingDisabled = Boolean(!await p1.execute(() => config.liveStreaming?.enabled))
19
+            || !process.env.YTUBE_TEST_STREAM_KEY;
20
+    });
21
+
22
+    it('start/stop function', async () => {
23
+        if (ctx.data.recordingDisabled) {
24
+            return;
25
+        }
26
+
27
+        await testRecordingStarted(true);
28
+        await testRecordingStopped(true);
29
+    });
30
+
31
+    it('start/stop command', async () => {
32
+        if (ctx.data.recordingDisabled) {
33
+            return;
34
+        }
35
+
36
+        await testRecordingStarted(false);
37
+        await testRecordingStopped(false);
38
+    });
39
+
40
+    it('start/stop Livestreaming command', async () => {
41
+        if (ctx.data.liveStreamingDisabled) {
42
+            return;
43
+        }
44
+
45
+        const { p1, webhooksProxy } = ctx;
46
+
47
+        await p1.switchToAPI();
48
+        await p1.getIframeAPI().addEventListener('recordingStatusChanged');
49
+
50
+        await p1.getIframeAPI().executeCommand('startRecording', {
51
+            youtubeBroadcastID: process.env.YTUBE_TEST_BROADCAST_ID,
52
+            mode: 'stream',
53
+            youtubeStreamKey: process.env.YTUBE_TEST_STREAM_KEY
54
+        });
55
+
56
+        if (webhooksProxy) {
57
+            const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
58
+            const liveStreamEvent: {
59
+                customerId: string;
60
+                eventType: string;
61
+            } = await webhooksProxy.waitForEvent('LIVE_STREAM_STARTED', 15000);
62
+
63
+            expect('LIVE_STREAM_STARTED').toBe(liveStreamEvent.eventType);
64
+            expect(liveStreamEvent.customerId).toBe(customerId);
65
+        }
66
+
67
+        const statusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
68
+
69
+        expect(statusEvent.mode).toBe('stream');
70
+        expect(statusEvent.on).toBe(true);
71
+
72
+        if (process.env.YTUBE_TEST_BROADCAST_ID) {
73
+            const liveStreamUrl = await p1.getIframeAPI().getLivestreamUrl();
74
+
75
+            expect(liveStreamUrl.livestreamUrl).toBeDefined();
76
+        }
77
+
78
+        await p1.getIframeAPI().executeCommand('stopRecording', 'stream');
79
+
80
+        if (webhooksProxy) {
81
+            const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
82
+            const liveStreamEvent: {
83
+                customerId: string;
84
+                eventType: string;
85
+            } = await webhooksProxy.waitForEvent('LIVE_STREAM_ENDED');
86
+
87
+            expect('LIVE_STREAM_ENDED').toBe(liveStreamEvent.eventType);
88
+            expect(liveStreamEvent.customerId).toBe(customerId);
89
+        }
90
+
91
+        const stoppedStatusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
92
+
93
+        expect(stoppedStatusEvent.mode).toBe('stream');
94
+        expect(stoppedStatusEvent.on).toBe(false);
95
+    });
96
+});
97
+
98
+/**
99
+ * Checks if the recording is started.
100
+ * @param command
101
+ */
102
+async function testRecordingStarted(command: boolean) {
103
+    const { p1, webhooksProxy } = ctx;
104
+
105
+    await p1.switchToAPI();
106
+    await p1.getIframeAPI().addEventListener('recordingStatusChanged');
107
+    await p1.getIframeAPI().addEventListener('recordingLinkAvailable');
108
+
109
+    if (command) {
110
+        await p1.getIframeAPI().executeCommand('startRecording', {
111
+            mode: 'file'
112
+        });
113
+    } else {
114
+        await p1.getIframeAPI().startRecording({
115
+            mode: 'file'
116
+        });
117
+    }
118
+
119
+    if (webhooksProxy) {
120
+        const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
121
+        const recordingEvent: {
122
+            customerId: string;
123
+            eventType: string;
124
+        } = await webhooksProxy.waitForEvent('RECORDING_STARTED', 15000);
125
+
126
+        expect('RECORDING_STARTED').toBe(recordingEvent.eventType);
127
+        expect(recordingEvent.customerId).toBe(customerId);
128
+
129
+        webhooksProxy?.clearCache();
130
+    }
131
+
132
+    const statusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
133
+
134
+    expect(statusEvent.mode).toBe('file');
135
+    expect(statusEvent.on).toBe(true);
136
+
137
+    const linkEvent = (await p1.getIframeAPI().getEventResult('recordingLinkAvailable'));
138
+
139
+    expect(linkEvent.link.startsWith('https://')).toBe(true);
140
+    expect(linkEvent.link.includes(process.env.IFRAME_TENANT)).toBe(true);
141
+    expect(linkEvent.ttl > 0).toBe(true);
142
+}
143
+
144
+/**
145
+ * Checks if the recording is stopped.
146
+ * @param command
147
+ */
148
+async function testRecordingStopped(command: boolean) {
149
+    const { p1, webhooksProxy } = ctx;
150
+
151
+    await p1.switchToAPI();
152
+    if (command) {
153
+        await p1.getIframeAPI().executeCommand('stopRecording', 'file');
154
+    } else {
155
+        await p1.getIframeAPI().stopRecording('file');
156
+    }
157
+
158
+    if (webhooksProxy) {
159
+        const customerId = process.env.IFRAME_TENANT?.replace('vpaas-magic-cookie-', '');
160
+        const liveStreamEvent: {
161
+            customerId: string;
162
+            eventType: string;
163
+        } = await webhooksProxy.waitForEvent('RECORDING_ENDED');
164
+
165
+        expect('RECORDING_ENDED').toBe(liveStreamEvent.eventType);
166
+        expect(liveStreamEvent.customerId).toBe(customerId);
167
+
168
+        const recordingUploadedEvent: {
169
+            customerId: string;
170
+            data: {
171
+                initiatorId: string;
172
+                participants: Array<string>;
173
+            };
174
+            eventType: string;
175
+        } = await webhooksProxy.waitForEvent('RECORDING_UPLOADED', 20000);
176
+
177
+        const jwtPayload = ctx.data[`${p1.name}-jwt-payload`];
178
+
179
+        expect(recordingUploadedEvent.data.initiatorId).toBe(jwtPayload?.context?.user?.id);
180
+        expect(recordingUploadedEvent.data.participants.some(
181
+            // @ts-ignore
182
+            e => e.id === jwtPayload?.context?.user?.id)).toBe(true);
183
+
184
+        webhooksProxy?.clearCache();
185
+    }
186
+
187
+    const statusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
188
+
189
+    expect(statusEvent.mode).toBe('file');
190
+    expect(statusEvent.on).toBe(false);
191
+
192
+    await p1.getIframeAPI().clearEventResults('recordingStatusChanged');
193
+}

+ 1
- 0
tests/wdio.firefox.conf.ts View File

23
     'specs/2way/iFrameApiParticipantsPresence.spec.ts', // FF does not support uploading files (uploadFile)
23
     'specs/2way/iFrameApiParticipantsPresence.spec.ts', // FF does not support uploading files (uploadFile)
24
     'specs/2way/iFrameApiTranscriptions.spec.ts',
24
     'specs/2way/iFrameApiTranscriptions.spec.ts',
25
     'specs/alone/iFrameApiInvite.spec.ts',
25
     'specs/alone/iFrameApiInvite.spec.ts',
26
+    'specs/alone/iFrameApiRecording.spec.ts',
26
 
27
 
27
     // FF does not support setting a file as mic input, no dominant speaker events
28
     // FF does not support setting a file as mic input, no dominant speaker events
28
     'specs/3way/activeSpeaker.spec.ts',
29
     'specs/3way/activeSpeaker.spec.ts',

Loading…
Cancel
Save