import { isEqual } from 'lodash-es'; import { P1, P2, Participant } from '../../helpers/Participant'; import { setTestProperties } from '../../helpers/TestProperties'; import { config as testsConfig } from '../../helpers/TestsConfig'; import { ensureTwoParticipants, parseJid } from '../../helpers/participants'; setTestProperties(__filename, { useIFrameApi: true, useWebhookProxy: true, usesBrowsers: [ 'p1', 'p2' ] }); /** * Tests PARTICIPANT_LEFT webhook. */ async function checkParticipantLeftHook(p: Participant, reason: string, checkId = false, conferenceJid: string) { const { webhooksProxy } = ctx; if (webhooksProxy) { // PARTICIPANT_LEFT webhook // @ts-ignore const event: { customerId: string; data: { conference: string; disconnectReason: string; group: string; id: string; isBreakout: boolean; name: string; participantId: string; }; eventType: string; } = await webhooksProxy.waitForEvent('PARTICIPANT_LEFT'); expect('PARTICIPANT_LEFT').toBe(event.eventType); expect(event.data.conference).toBe(conferenceJid); expect(event.data.disconnectReason).toBe(reason); expect(event.data.isBreakout).toBe(false); expect(event.data.participantId).toBe(await p.getEndpointId()); expect(event.data.name).toBe(p.name); if (checkId) { const jwtPayload = p.getToken()?.payload; expect(event.data.id).toBe(jwtPayload?.context?.user?.id); expect(event.data.group).toBe(jwtPayload?.context?.group); expect(event.customerId).toBe(testsConfig.iframe.customerId); } } } describe('Participants presence', () => { let conferenceJid: string = ''; it('joining the meeting', async () => { // ensure 2 participants one moderator and one guest, we will load both with iframeAPI await ensureTwoParticipants(); const { p1, p2, webhooksProxy } = ctx; if (await p1.execute(() => config.disableIframeAPI)) { // skip the test if iframeAPI is disabled ctx.skipSuiteTests = true; return; } // let's populate endpoint ids await Promise.all([ p1.getEndpointId(), p2.getEndpointId() ]); await p1.switchToAPI(); await p2.switchToAPI(); expect(await p1.getIframeAPI().getEventResult('isModerator')).toBe(true); expect(await p2.getIframeAPI().getEventResult('isModerator')).toBe(false); expect(await p1.getIframeAPI().getEventResult('videoConferenceJoined')).toBeDefined(); expect(await p2.getIframeAPI().getEventResult('videoConferenceJoined')).toBeDefined(); if (webhooksProxy) { // USAGE webhook // @ts-ignore const event: { data: [ { participantId: string; } ]; eventType: string; } = await webhooksProxy.waitForEvent('USAGE'); expect('USAGE').toBe(event.eventType); const p1EpId = await p1.getEndpointId(); const p2EpId = await p2.getEndpointId(); expect(event.data.filter(d => d.participantId === p1EpId || d.participantId === p2EpId).length).toBe(2); } }); it('participants info', async () => { const { p1, roomName, webhooksProxy } = ctx; const roomsInfo = (await p1.getIframeAPI().getRoomsInfo()).rooms[0]; expect(roomsInfo).toBeDefined(); expect(roomsInfo.isMainRoom).toBe(true); expect(roomsInfo.id).toBeDefined(); const { node: roomNode } = parseJid(roomsInfo.id); expect(roomNode).toBe(roomName); const { node, resource } = parseJid(roomsInfo.jid); conferenceJid = roomsInfo.jid.substring(0, roomsInfo.jid.indexOf('/')); const p1EpId = await p1.getEndpointId(); expect(node).toBe(roomName); expect(resource).toBe(p1EpId); expect(roomsInfo.participants.length).toBe(2); expect(await p1.getIframeAPI().getNumberOfParticipants()).toBe(2); if (webhooksProxy) { // ROOM_CREATED webhook // @ts-ignore const event: { data: { conference: string; isBreakout: boolean; }; eventType: string; } = await webhooksProxy.waitForEvent('ROOM_CREATED'); expect('ROOM_CREATED').toBe(event.eventType); expect(event.data.conference).toBe(conferenceJid); expect(event.data.isBreakout).toBe(false); } } ); it('participants pane', async () => { const { p1 } = ctx; await p1.switchToAPI(); expect(await p1.getIframeAPI().isParticipantsPaneOpen()).toBe(false); await p1.getIframeAPI().addEventListener('participantsPaneToggled'); await p1.getIframeAPI().executeCommand('toggleParticipantsPane', true); expect(await p1.getIframeAPI().isParticipantsPaneOpen()).toBe(true); expect((await p1.getIframeAPI().getEventResult('participantsPaneToggled'))?.open).toBe(true); await p1.getIframeAPI().executeCommand('toggleParticipantsPane', false); expect(await p1.getIframeAPI().isParticipantsPaneOpen()).toBe(false); expect((await p1.getIframeAPI().getEventResult('participantsPaneToggled'))?.open).toBe(false); }); it('grant moderator', async () => { const { p1, p2, webhooksProxy } = ctx; const p2EpId = await p2.getEndpointId(); await p1.getIframeAPI().clearEventResults('participantRoleChanged'); await p2.getIframeAPI().clearEventResults('participantRoleChanged'); await p1.getIframeAPI().executeCommand('grantModerator', p2EpId); await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('isModerator'), { timeout: 3000, timeoutMsg: 'Moderator role not granted' }); type RoleChangedEvent = { id: string; role: string; }; const event1: RoleChangedEvent = await p1.driver.waitUntil( () => p1.getIframeAPI().getEventResult('participantRoleChanged'), { timeout: 3000, timeoutMsg: 'Role was not update on p1 side' }); expect(event1?.id).toBe(p2EpId); expect(event1?.role).toBe('moderator'); const event2: RoleChangedEvent = await p2.driver.waitUntil( () => p2.getIframeAPI().getEventResult('participantRoleChanged'), { timeout: 3000, timeoutMsg: 'Role was not update on p2 side' }); expect(event2?.id).toBe(p2EpId); expect(event2?.role).toBe('moderator'); if (webhooksProxy) { // ROLE_CHANGED webhook // @ts-ignore const event: { data: { grantedBy: { participantId: string; }; grantedTo: { participantId: string; }; role: string; }; eventType: string; } = await webhooksProxy.waitForEvent('ROLE_CHANGED'); expect('ROLE_CHANGED').toBe(event.eventType); expect(event.data.role).toBe('moderator'); expect(event.data.grantedBy.participantId).toBe(await p1.getEndpointId()); expect(event.data.grantedTo.participantId).toBe(await p2.getEndpointId()); } }); it('kick participant', async () => { // we want to join second participant with token, so we can check info in webhook await ctx.p2.getIframeAPI().addEventListener('videoConferenceLeft'); await ctx.p2.switchToAPI(); await ctx.p2.getIframeAPI().executeCommand('hangup'); await ctx.p2.driver.waitUntil(() => ctx.p2.getIframeAPI().getEventResult('videoConferenceLeft'), { timeout: 4000, timeoutMsg: 'videoConferenceLeft not received' }); await ensureTwoParticipants({ preferGenerateToken: true }); const { p1, p2, roomName, webhooksProxy } = ctx; webhooksProxy?.clearCache(); const p1EpId = await p1.getEndpointId(); const p2EpId = await p2.getEndpointId(); const p1DisplayName = await p1.getLocalDisplayName(); const p2DisplayName = await p2.getLocalDisplayName(); await p1.switchToAPI(); await p2.switchToAPI(); const roomsInfo = (await p1.getIframeAPI().getRoomsInfo()).rooms[0]; conferenceJid = roomsInfo.jid.substring(0, roomsInfo.jid.indexOf('/')); await p1.getIframeAPI().addEventListener('participantKickedOut'); await p2.getIframeAPI().addEventListener('participantKickedOut'); await p2.getIframeAPI().clearEventResults('videoConferenceLeft'); await p2.getIframeAPI().addEventListener('videoConferenceLeft'); await p1.getIframeAPI().executeCommand('kickParticipant', p2EpId); const eventP1 = await p1.driver.waitUntil(() => p1.getIframeAPI().getEventResult('participantKickedOut'), { timeout: 2000, timeoutMsg: 'participantKickedOut event not received on p1 side' }); const eventP2 = await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('participantKickedOut'), { timeout: 2000, timeoutMsg: 'participantKickedOut event not received on p2 side' }); await checkParticipantLeftHook(p2, 'kicked', true, conferenceJid); expect(eventP1).toBeDefined(); expect(eventP2).toBeDefined(); expect(isEqual(eventP1, { kicked: { id: p2EpId, local: false, name: p2DisplayName }, kicker: { id: p1EpId, local: true, name: p1DisplayName } })).toBe(true); expect(isEqual(eventP2, { kicked: { id: 'local', local: true, name: p2DisplayName }, kicker: { id: p1EpId, name: p1DisplayName } })).toBe(true); const eventConferenceLeftP2 = await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('videoConferenceLeft'), { timeout: 4000, timeoutMsg: 'videoConferenceLeft not received' }); expect(eventConferenceLeftP2).toBeDefined(); expect(eventConferenceLeftP2.roomName).toBe(roomName); }); it('join after kick', async () => { const { p1, webhooksProxy } = ctx; await p1.getIframeAPI().addEventListener('participantJoined'); await p1.getIframeAPI().addEventListener('participantMenuButtonClick'); webhooksProxy?.clearCache(); // join again await ensureTwoParticipants(); const { p2 } = ctx; if (webhooksProxy) { // PARTICIPANT_JOINED webhook // @ts-ignore const event: { data: { conference: string; isBreakout: boolean; moderator: boolean; name: string; participantId: string; }; eventType: string; } = await webhooksProxy.waitForEvent('PARTICIPANT_JOINED'); expect('PARTICIPANT_JOINED').toBe(event.eventType); expect(event.data.conference).toBe(conferenceJid); expect(event.data.isBreakout).toBe(false); expect(event.data.moderator).toBe(false); expect(event.data.name).toBe(await p2.getLocalDisplayName()); expect(event.data.participantId).toBe(await p2.getEndpointId()); expect(event.data.name).toBe(p2.name); } await p1.switchToAPI(); const event = await p1.driver.waitUntil(() => p1.getIframeAPI().getEventResult('participantJoined'), { timeout: 2000, timeoutMsg: 'participantJoined not received' }); const p2DisplayName = await p2.getLocalDisplayName(); expect(event).toBeDefined(); expect(event.id).toBe(await p2.getEndpointId()); expect(event.displayName).toBe(p2DisplayName); expect(event.formattedDisplayName).toBe(p2DisplayName); }); it('overwrite names', async () => { const { p1, p2 } = ctx; const p1EpId = await p1.getEndpointId(); const p2EpId = await p2.getEndpointId(); const newP1Name = P1; const newP2Name = P2; const newNames: ({ id: string; name: string; })[] = [ { id: p2EpId, name: newP2Name }, { id: p1EpId, name: newP1Name } ]; await p1.getIframeAPI().executeCommand('overwriteNames', newNames); await p1.switchInPage(); expect(await p1.getLocalDisplayName()).toBe(newP1Name); expect(await p1.getFilmstrip().getRemoteDisplayName(p2EpId)).toBe(newP2Name); }); it('hangup', async () => { const { p1, p2, roomName } = ctx; await p1.switchToAPI(); await p2.switchToAPI(); await p2.getIframeAPI().clearEventResults('videoConferenceLeft'); await p2.getIframeAPI().addEventListener('videoConferenceLeft'); await p2.getIframeAPI().addEventListener('readyToClose'); await p2.getIframeAPI().executeCommand('hangup'); const eventConferenceLeftP2 = await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('videoConferenceLeft'), { timeout: 4000, timeoutMsg: 'videoConferenceLeft not received' }); expect(eventConferenceLeftP2).toBeDefined(); expect(eventConferenceLeftP2.roomName).toBe(roomName); await checkParticipantLeftHook(p2, 'left', false, conferenceJid); const eventReadyToCloseP2 = await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('readyToClose'), { timeout: 2000, timeoutMsg: 'readyToClose not received' }); expect(eventReadyToCloseP2).toBeDefined(); }); it('dispose conference', async () => { const { p1, roomName, webhooksProxy } = ctx; await p1.switchToAPI(); await p1.getIframeAPI().clearEventResults('videoConferenceLeft'); await p1.getIframeAPI().addEventListener('videoConferenceLeft'); await p1.getIframeAPI().addEventListener('readyToClose'); await p1.getIframeAPI().executeCommand('hangup'); const eventConferenceLeft = await p1.driver.waitUntil(() => p1.getIframeAPI().getEventResult('videoConferenceLeft'), { timeout: 4000, timeoutMsg: 'videoConferenceLeft not received' }); expect(eventConferenceLeft).toBeDefined(); expect(eventConferenceLeft.roomName).toBe(roomName); await checkParticipantLeftHook(p1, 'left', true, conferenceJid); if (webhooksProxy) { // ROOM_DESTROYED webhook // @ts-ignore const event: { data: { conference: string; isBreakout: boolean; }; eventType: string; } = await webhooksProxy.waitForEvent('ROOM_DESTROYED'); expect('ROOM_DESTROYED').toBe(event.eventType); expect(event.data.conference).toBe(conferenceJid); expect(event.data.isBreakout).toBe(false); } const eventReadyToClose = await p1.driver.waitUntil(() => p1.getIframeAPI().getEventResult('readyToClose'), { timeout: 2000, timeoutMsg: 'readyToClose not received' }); expect(eventReadyToClose).toBeDefined(); // dispose await p1.getIframeAPI().dispose(); // check there is no iframe on the page await p1.driver.$('iframe').waitForExist({ reverse: true, timeout: 2000, timeoutMsg: 'iframe is still on the page' }); }); });