import { Participant } from '../../helpers/Participant'; import { setTestProperties } from '../../helpers/TestProperties'; import { config as testsConfig } from '../../helpers/TestsConfig'; import WebhookProxy from '../../helpers/WebhookProxy'; import { expectations } from '../../helpers/expectations'; import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas'; setTestProperties(__filename, { requireWebhookProxy: true, useJaas: true }); /** * Tests the recording and live-streaming functionality of JaaS (including relevant webhooks) exercising the iFrame API * commands and functions. * TODO: also assert "this meeting is being recorded" notifications are show/played? */ describe('Recording and live-streaming', () => { const tenant = testsConfig.jaas.tenant; const customerId = tenant?.replace('vpaas-magic-cookie-', ''); let recordingEnabled: boolean; let liveStreamingEnabled: boolean; let p: Participant; let webhooksProxy: WebhookProxy; it('setup', async () => { webhooksProxy = ctx.webhooksProxy; p = await joinJaasMuc({ iFrameApi: true, token: t({ moderator: true }) }, { roomName: ctx.roomName }); recordingEnabled = Boolean(await p.execute(() => config.recordingService?.enabled)); expect(recordingEnabled).toBe(expectations.jaas.recordingEnabled); liveStreamingEnabled = Boolean(await p.execute(() => config.liveStreaming?.enabled)); expect(liveStreamingEnabled).toBe(expectations.jaas.liveStreamingEnabled); if (liveStreamingEnabled && !process.env.YTUBE_TEST_STREAM_KEY) { liveStreamingEnabled = false; console.log('Skipping live-streaming tests because YTUBE_TEST_STREAM_KEY is not set.'); } await p.switchToMainFrame(); }); /** * Starts recording and asserts that the expected iFrame and JaaS events are received. * @param command whether to use the "command" or the "function" iFrame API. */ async function startRecording(command: boolean) { await p.getIframeAPI().addEventListener('recordingStatusChanged'); await p.getIframeAPI().addEventListener('recordingLinkAvailable'); if (command) { await p.getIframeAPI().executeCommand('startRecording', { mode: 'file' }); } else { await p.getIframeAPI().startRecording({ mode: 'file' }); } const jaasEvent: { customerId: string; eventType: string; } = await webhooksProxy.waitForEvent('RECORDING_STARTED'); expect('RECORDING_STARTED').toBe(jaasEvent.eventType); expect(jaasEvent.customerId).toBe(customerId); webhooksProxy.clearCache(); const iFrameEvent = await p.driver.waitUntil(() => p.getIframeAPI().getEventResult('recordingStatusChanged'), { timeout: 5000, timeoutMsg: 'recordingStatusChanged event not received' }); expect(iFrameEvent.mode).toBe('file'); expect(iFrameEvent.on).toBe(true); const linkEvent = await p.driver.waitUntil(() => p.getIframeAPI().getEventResult('recordingLinkAvailable'), { timeout: 5000, timeoutMsg: 'recordingLinkAvailable event not received' }); expect(linkEvent.link.startsWith('https://')).toBe(true); expect(linkEvent.link.includes(tenant)).toBe(true); expect(linkEvent.ttl > 0).toBe(true); } /** * Stops recording and asserts that the expected iFrame and JaaS events are received. * @param command whether to use the "command" or the "function" iFrame API. */ async function stopRecording(command: boolean) { if (command) { await p.getIframeAPI().executeCommand('stopRecording', 'file'); } else { await p.getIframeAPI().stopRecording('file'); } const jaasEndedEvent: { customerId: string; eventType: string; } = await webhooksProxy.waitForEvent('RECORDING_ENDED'); expect('RECORDING_ENDED').toBe(jaasEndedEvent.eventType); expect(jaasEndedEvent.customerId).toBe(customerId); const jaasUploadedEvent: { customerId: string; data: { initiatorId: string; participants: Array; }; eventType: string; } = await webhooksProxy.waitForEvent('RECORDING_UPLOADED'); const jwtPayload = p.getToken()?.payload; expect(jaasUploadedEvent.data.initiatorId).toBe(jwtPayload?.context?.user?.id); expect(jaasUploadedEvent.data.participants.some( // @ts-ignore e => e.id === jwtPayload?.context?.user?.id)).toBe(true); webhooksProxy.clearCache(); const iFrameEvent = (await p.getIframeAPI().getEventResult('recordingStatusChanged')); expect(iFrameEvent.mode).toBe('file'); expect(iFrameEvent.on).toBe(false); await p.getIframeAPI().clearEventResults('recordingStatusChanged'); } it('start/stop recording using the iFrame command', async () => { if (!recordingEnabled) { return; } await startRecording(true); await stopRecording(true); // to avoid rate limits await p.driver.pause(30000); }); it('start/stop recording using the iFrame function', async () => { if (!recordingEnabled) { return; } await startRecording(false); await stopRecording(false); // to avoid rate limits await p.driver.pause(30000); }); it('start/stop live-streaming using the iFrame command', async () => { if (!liveStreamingEnabled) { return; } await p.getIframeAPI().addEventListener('recordingStatusChanged'); await p.getIframeAPI().executeCommand('startRecording', { youtubeBroadcastID: process.env.YTUBE_TEST_BROADCAST_ID, mode: 'stream', youtubeStreamKey: process.env.YTUBE_TEST_STREAM_KEY }); const jaasEvent: { customerId: string; eventType: string; } = await webhooksProxy.waitForEvent('LIVE_STREAM_STARTED'); expect('LIVE_STREAM_STARTED').toBe(jaasEvent.eventType); expect(jaasEvent.customerId).toBe(customerId); const iFrameEvent = (await p.getIframeAPI().getEventResult('recordingStatusChanged')); expect(iFrameEvent.mode).toBe('stream'); expect(iFrameEvent.on).toBe(true); if (process.env.YTUBE_TEST_BROADCAST_ID) { const liveStreamUrl = await p.getIframeAPI().getLivestreamUrl(); expect(liveStreamUrl.livestreamUrl).toBeDefined(); } await p.getIframeAPI().executeCommand('stopRecording', 'stream'); const jaasEndedEvent: { customerId: string; eventType: string; } = await webhooksProxy.waitForEvent('LIVE_STREAM_ENDED'); expect(jaasEndedEvent.eventType).toBe('LIVE_STREAM_ENDED'); expect(jaasEndedEvent.customerId).toBe(customerId); const iFrameEndedEvent = (await p.getIframeAPI().getEventResult('recordingStatusChanged')); expect(iFrameEndedEvent.mode).toBe('stream'); expect(iFrameEndedEvent.on).toBe(false); }); });