This commit is contained in:
198
tests/specs/iframe/chat.spec.ts
Normal file
198
tests/specs/iframe/chat.spec.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import { expect } from '@wdio/globals';
|
||||
|
||||
import type { Participant } from '../../helpers/Participant';
|
||||
import { setTestProperties } from '../../helpers/TestProperties';
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
import { fetchJson } from '../../helpers/utils';
|
||||
|
||||
setTestProperties(__filename, {
|
||||
useIFrameApi: true,
|
||||
useWebhookProxy: true,
|
||||
usesBrowsers: [ 'p1', 'p2' ]
|
||||
});
|
||||
|
||||
describe('Chat', () => {
|
||||
it('joining the meeting', async () => {
|
||||
await ensureTwoParticipants();
|
||||
|
||||
const { p1, p2 } = 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()
|
||||
]);
|
||||
});
|
||||
|
||||
it('send message', async () => {
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p2.switchToAPI();
|
||||
|
||||
await p2.getIframeAPI().addEventListener('chatUpdated');
|
||||
await p2.getIframeAPI().addEventListener('incomingMessage');
|
||||
await p1.getIframeAPI().addEventListener('outgoingMessage');
|
||||
|
||||
const testMessage = 'Hello world';
|
||||
|
||||
await p1.getIframeAPI().executeCommand('sendChatMessage', testMessage);
|
||||
|
||||
const chatUpdatedEvent: {
|
||||
isOpen: boolean;
|
||||
unreadCount: number;
|
||||
} = await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('chatUpdated'), {
|
||||
timeout: 3000,
|
||||
timeoutMsg: 'Chat was not updated'
|
||||
});
|
||||
|
||||
expect(chatUpdatedEvent).toEqual({
|
||||
isOpen: false,
|
||||
unreadCount: 1
|
||||
});
|
||||
|
||||
const incomingMessageEvent: {
|
||||
from: string;
|
||||
message: string;
|
||||
nick: string;
|
||||
privateMessage: boolean;
|
||||
} = await p2.getIframeAPI().getEventResult('incomingMessage');
|
||||
|
||||
expect(incomingMessageEvent).toEqual({
|
||||
from: await p1.getEndpointId(),
|
||||
message: testMessage,
|
||||
nick: p1.name,
|
||||
privateMessage: false
|
||||
});
|
||||
|
||||
const outgoingMessageEvent: {
|
||||
message: string;
|
||||
privateMessage: boolean;
|
||||
} = await p1.getIframeAPI().getEventResult('outgoingMessage');
|
||||
|
||||
expect(outgoingMessageEvent).toEqual({
|
||||
message: testMessage,
|
||||
privateMessage: false
|
||||
});
|
||||
|
||||
await p1.getIframeAPI().clearEventResults('outgoingMessage');
|
||||
await p2.getIframeAPI().clearEventResults('chatUpdated');
|
||||
await p2.getIframeAPI().clearEventResults('incomingMessage');
|
||||
});
|
||||
|
||||
it('toggle chat', async () => {
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
await p2.getIframeAPI().executeCommand('toggleChat');
|
||||
|
||||
await testSendGroupMessageWithChatOpen(p1, p2);
|
||||
|
||||
await p1.getIframeAPI().clearEventResults('outgoingMessage');
|
||||
await p2.getIframeAPI().clearEventResults('chatUpdated');
|
||||
await p2.getIframeAPI().clearEventResults('incomingMessage');
|
||||
});
|
||||
|
||||
it('private chat', async () => {
|
||||
const { p1, p2 } = ctx;
|
||||
const testMessage = 'Hello private world!';
|
||||
const p2Id = await p2.getEndpointId();
|
||||
const p1Id = await p1.getEndpointId();
|
||||
|
||||
await p1.getIframeAPI().executeCommand('initiatePrivateChat', p2Id);
|
||||
await p1.getIframeAPI().executeCommand('sendChatMessage', testMessage, p2Id);
|
||||
|
||||
const incomingMessageEvent = await p2.driver.waitUntil(
|
||||
() => p2.getIframeAPI().getEventResult('incomingMessage'), {
|
||||
timeout: 3000,
|
||||
timeoutMsg: 'Chat was not received'
|
||||
});
|
||||
|
||||
expect(incomingMessageEvent).toEqual({
|
||||
from: p1Id,
|
||||
message: testMessage,
|
||||
nick: p1.name,
|
||||
privateMessage: true
|
||||
});
|
||||
|
||||
expect(await p1.getIframeAPI().getEventResult('outgoingMessage')).toEqual({
|
||||
message: testMessage,
|
||||
privateMessage: true
|
||||
});
|
||||
|
||||
await p1.getIframeAPI().executeCommand('cancelPrivateChat');
|
||||
|
||||
await p2.getIframeAPI().clearEventResults('chatUpdated');
|
||||
await p2.getIframeAPI().clearEventResults('incomingMessage');
|
||||
|
||||
await testSendGroupMessageWithChatOpen(p1, p2);
|
||||
});
|
||||
|
||||
it('chat upload chat', async () => {
|
||||
const { p1, p2, webhooksProxy } = ctx;
|
||||
|
||||
await p1.getIframeAPI().executeCommand('hangup');
|
||||
await p2.getIframeAPI().executeCommand('hangup');
|
||||
|
||||
if (webhooksProxy) {
|
||||
const event: {
|
||||
data: {
|
||||
preAuthenticatedLink: string;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('CHAT_UPLOADED');
|
||||
|
||||
expect('CHAT_UPLOADED').toBe(event.eventType);
|
||||
expect(event.data.preAuthenticatedLink).toBeDefined();
|
||||
|
||||
const uploadedChat: any = await fetchJson(event.data.preAuthenticatedLink);
|
||||
|
||||
expect(uploadedChat.messageType).toBe('CHAT');
|
||||
expect(uploadedChat.messages).toBeDefined();
|
||||
expect(uploadedChat.messages.length).toBe(3);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test sending a group message with the chat open.
|
||||
* @param p1
|
||||
* @param p2
|
||||
*/
|
||||
async function testSendGroupMessageWithChatOpen(p1: Participant, p2: Participant) {
|
||||
const testMessage = 'Hello world again';
|
||||
|
||||
await p1.getIframeAPI().executeCommand('sendChatMessage', testMessage);
|
||||
|
||||
const chatUpdatedEvent: {
|
||||
isOpen: boolean;
|
||||
unreadCount: number;
|
||||
} = await p2.driver.waitUntil(() => p2.getIframeAPI().getEventResult('chatUpdated'), {
|
||||
timeout: 3000,
|
||||
timeoutMsg: 'Chat was not updated'
|
||||
});
|
||||
|
||||
expect(chatUpdatedEvent).toEqual({
|
||||
isOpen: true,
|
||||
unreadCount: 0
|
||||
});
|
||||
|
||||
const incomingMessageEvent = await p2.driver.waitUntil(
|
||||
() => p2.getIframeAPI().getEventResult('incomingMessage'), {
|
||||
timeout: 3000,
|
||||
timeoutMsg: 'Chat was not received'
|
||||
});
|
||||
|
||||
expect(incomingMessageEvent).toEqual({
|
||||
from: await p1.getEndpointId(),
|
||||
message: testMessage,
|
||||
nick: p1.name,
|
||||
privateMessage: false
|
||||
});
|
||||
}
|
||||
214
tests/specs/iframe/invite.spec.ts
Normal file
214
tests/specs/iframe/invite.spec.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import type { Participant } from '../../helpers/Participant';
|
||||
import { setTestProperties } from '../../helpers/TestProperties';
|
||||
import { config as testsConfig } from '../../helpers/TestsConfig';
|
||||
import { ensureOneParticipant } from '../../helpers/participants';
|
||||
import {
|
||||
cleanup,
|
||||
dialIn,
|
||||
isDialInEnabled,
|
||||
waitForAudioFromDialInParticipant
|
||||
} from '../helpers/DialIn';
|
||||
|
||||
setTestProperties(__filename, {
|
||||
useIFrameApi: true,
|
||||
useWebhookProxy: true
|
||||
});
|
||||
|
||||
const customerId = testsConfig.iframe.customerId;
|
||||
|
||||
describe('Invite iframeAPI', () => {
|
||||
let dialInDisabled: boolean;
|
||||
let dialOutDisabled: boolean;
|
||||
let sipJibriDisabled: boolean;
|
||||
|
||||
it('join participant', async () => {
|
||||
await ensureOneParticipant();
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
// check for dial-in dial-out sip-jibri maybe
|
||||
if (await p1.execute(() => config.disableIframeAPI)) {
|
||||
// skip the test if iframeAPI is disabled
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dialOutDisabled = Boolean(!await p1.execute(() => config.dialOutAuthUrl));
|
||||
sipJibriDisabled = Boolean(!await p1.execute(() => config.inviteServiceUrl));
|
||||
|
||||
// check dial-in is enabled
|
||||
if (!await isDialInEnabled(ctx.p1) || !process.env.DIAL_IN_REST_URL) {
|
||||
dialInDisabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
it('dial-in', async () => {
|
||||
if (dialInDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { p1 } = ctx;
|
||||
const dialInPin = await p1.getDialInPin();
|
||||
|
||||
expect(dialInPin.length >= 8).toBe(true);
|
||||
|
||||
await dialIn(p1);
|
||||
|
||||
if (!await p1.isInMuc()) {
|
||||
// local participant did not join abort
|
||||
return;
|
||||
}
|
||||
|
||||
await waitForAudioFromDialInParticipant(p1);
|
||||
|
||||
await checkDialEvents(p1, 'in', 'DIAL_IN_STARTED', 'DIAL_IN_ENDED');
|
||||
});
|
||||
|
||||
it('dial-out', async () => {
|
||||
if (dialOutDisabled || !process.env.DIAL_OUT_URL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
|
||||
await p1.getIframeAPI().invitePhone(process.env.DIAL_OUT_URL);
|
||||
|
||||
await p1.switchInPage();
|
||||
|
||||
await p1.waitForParticipants(1);
|
||||
|
||||
await waitForAudioFromDialInParticipant(p1);
|
||||
|
||||
await checkDialEvents(p1, 'out', 'DIAL_OUT_STARTED', 'DIAL_OUT_ENDED');
|
||||
});
|
||||
|
||||
it('sip jibri', async () => {
|
||||
if (sipJibriDisabled || !process.env.SIP_JIBRI_DIAL_OUT_URL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
|
||||
await p1.getIframeAPI().inviteSIP(process.env.SIP_JIBRI_DIAL_OUT_URL);
|
||||
|
||||
await p1.switchInPage();
|
||||
|
||||
await p1.waitForParticipants(1);
|
||||
|
||||
await waitForAudioFromDialInParticipant(p1);
|
||||
|
||||
const { webhooksProxy } = ctx;
|
||||
|
||||
if (webhooksProxy) {
|
||||
const sipCallOutStartedEvent: {
|
||||
customerId: string;
|
||||
data: {
|
||||
participantFullJid: string;
|
||||
participantId: string;
|
||||
participantJid: string;
|
||||
sipAddress: string;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('SIP_CALL_OUT_STARTED');
|
||||
|
||||
expect('SIP_CALL_OUT_STARTED').toBe(sipCallOutStartedEvent.eventType);
|
||||
expect(sipCallOutStartedEvent.data.sipAddress).toBe(`sip:${process.env.SIP_JIBRI_DIAL_OUT_URL}`);
|
||||
expect(sipCallOutStartedEvent.customerId).toBe(customerId);
|
||||
|
||||
const participantId = sipCallOutStartedEvent.data.participantId;
|
||||
const participantJid = sipCallOutStartedEvent.data.participantJid;
|
||||
const participantFullJid = sipCallOutStartedEvent.data.participantFullJid;
|
||||
|
||||
await cleanup(p1);
|
||||
|
||||
const sipCallOutEndedEvent: {
|
||||
customerId: string;
|
||||
data: {
|
||||
direction: string;
|
||||
participantFullJid: string;
|
||||
participantId: string;
|
||||
participantJid: string;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('SIP_CALL_OUT_ENDED');
|
||||
|
||||
expect('SIP_CALL_OUT_ENDED').toBe(sipCallOutEndedEvent.eventType);
|
||||
expect(sipCallOutEndedEvent.customerId).toBe(customerId);
|
||||
expect(sipCallOutEndedEvent.data.participantFullJid).toBe(participantFullJid);
|
||||
expect(sipCallOutEndedEvent.data.participantId).toBe(participantId);
|
||||
expect(sipCallOutEndedEvent.data.participantJid).toBe(participantJid);
|
||||
} else {
|
||||
await cleanup(p1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks the dial events for a participant and clean up at the end.
|
||||
* @param participant
|
||||
* @param startedEventName
|
||||
* @param endedEventName
|
||||
* @param direction
|
||||
*/
|
||||
async function checkDialEvents(participant: Participant, direction: string, startedEventName: string, endedEventName: string) {
|
||||
const { webhooksProxy } = ctx;
|
||||
|
||||
if (webhooksProxy) {
|
||||
const dialInStartedEvent: {
|
||||
customerId: string;
|
||||
data: {
|
||||
direction: string;
|
||||
participantFullJid: string;
|
||||
participantId: string;
|
||||
participantJid: string;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent(startedEventName);
|
||||
|
||||
expect(startedEventName).toBe(dialInStartedEvent.eventType);
|
||||
expect(dialInStartedEvent.data.direction).toBe(direction);
|
||||
expect(dialInStartedEvent.customerId).toBe(customerId);
|
||||
|
||||
const participantId = dialInStartedEvent.data.participantId;
|
||||
const participantJid = dialInStartedEvent.data.participantJid;
|
||||
const participantFullJid = dialInStartedEvent.data.participantFullJid;
|
||||
|
||||
const usageEvent: {
|
||||
customerId: string;
|
||||
data: any;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('USAGE');
|
||||
|
||||
expect('USAGE').toBe(usageEvent.eventType);
|
||||
expect(usageEvent.customerId).toBe(customerId);
|
||||
|
||||
expect(usageEvent.data.some((el: any) =>
|
||||
el.participantId === participantId && el.callDirection === direction)).toBe(true);
|
||||
|
||||
await cleanup(participant);
|
||||
|
||||
const dialInEndedEvent: {
|
||||
customerId: string;
|
||||
data: {
|
||||
direction: string;
|
||||
participantFullJid: string;
|
||||
participantId: string;
|
||||
participantJid: string;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent(endedEventName);
|
||||
|
||||
expect(endedEventName).toBe(dialInEndedEvent.eventType);
|
||||
expect(dialInEndedEvent.customerId).toBe(customerId);
|
||||
expect(dialInEndedEvent.data.participantFullJid).toBe(participantFullJid);
|
||||
expect(dialInEndedEvent.data.participantId).toBe(participantId);
|
||||
expect(dialInEndedEvent.data.participantJid).toBe(participantJid);
|
||||
} else {
|
||||
await cleanup(participant);
|
||||
}
|
||||
}
|
||||
477
tests/specs/iframe/participantsPresence.spec.ts
Normal file
477
tests/specs/iframe/participantsPresence.spec.ts
Normal file
@@ -0,0 +1,477 @@
|
||||
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'
|
||||
});
|
||||
});
|
||||
});
|
||||
207
tests/specs/iframe/recording.spec.ts
Normal file
207
tests/specs/iframe/recording.spec.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
import { setTestProperties } from '../../helpers/TestProperties';
|
||||
import { config as testsConfig } from '../../helpers/TestsConfig';
|
||||
import { ensureOneParticipant } from '../../helpers/participants';
|
||||
|
||||
setTestProperties(__filename, {
|
||||
useIFrameApi: true,
|
||||
useWebhookProxy: true
|
||||
});
|
||||
|
||||
const { tenant, customerId } = testsConfig.iframe;
|
||||
|
||||
describe('Recording', () => {
|
||||
let recordingDisabled: boolean;
|
||||
let liveStreamingDisabled: boolean;
|
||||
|
||||
it('join participant', async () => {
|
||||
await ensureOneParticipant();
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
// check for dial-in dial-out sip-jibri maybe
|
||||
if (await p1.execute(() => config.disableIframeAPI)) {
|
||||
// skip the test if iframeAPI is disabled
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
recordingDisabled = Boolean(!await p1.execute(() => config.recordingService?.enabled));
|
||||
liveStreamingDisabled = Boolean(!await p1.execute(() => config.liveStreaming?.enabled))
|
||||
|| !process.env.YTUBE_TEST_STREAM_KEY;
|
||||
});
|
||||
|
||||
it('start/stop function', async () => {
|
||||
if (recordingDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await testRecordingStarted(true);
|
||||
await testRecordingStopped(true);
|
||||
|
||||
// to avoid limits
|
||||
await ctx.p1.driver.pause(30000);
|
||||
});
|
||||
|
||||
it('start/stop command', async () => {
|
||||
if (recordingDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await testRecordingStarted(false);
|
||||
await testRecordingStopped(false);
|
||||
|
||||
// to avoid limits
|
||||
await ctx.p1.driver.pause(30000);
|
||||
});
|
||||
|
||||
it('start/stop Livestreaming command', async () => {
|
||||
if (liveStreamingDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { p1, webhooksProxy } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.getIframeAPI().addEventListener('recordingStatusChanged');
|
||||
|
||||
await p1.getIframeAPI().executeCommand('startRecording', {
|
||||
youtubeBroadcastID: process.env.YTUBE_TEST_BROADCAST_ID,
|
||||
mode: 'stream',
|
||||
youtubeStreamKey: process.env.YTUBE_TEST_STREAM_KEY
|
||||
});
|
||||
|
||||
if (webhooksProxy) {
|
||||
const liveStreamEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('LIVE_STREAM_STARTED');
|
||||
|
||||
expect('LIVE_STREAM_STARTED').toBe(liveStreamEvent.eventType);
|
||||
expect(liveStreamEvent.customerId).toBe(customerId);
|
||||
}
|
||||
|
||||
const statusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(statusEvent.mode).toBe('stream');
|
||||
expect(statusEvent.on).toBe(true);
|
||||
|
||||
if (process.env.YTUBE_TEST_BROADCAST_ID) {
|
||||
const liveStreamUrl = await p1.getIframeAPI().getLivestreamUrl();
|
||||
|
||||
expect(liveStreamUrl.livestreamUrl).toBeDefined();
|
||||
}
|
||||
|
||||
await p1.getIframeAPI().executeCommand('stopRecording', 'stream');
|
||||
|
||||
if (webhooksProxy) {
|
||||
const liveStreamEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('LIVE_STREAM_ENDED');
|
||||
|
||||
expect('LIVE_STREAM_ENDED').toBe(liveStreamEvent.eventType);
|
||||
expect(liveStreamEvent.customerId).toBe(customerId);
|
||||
}
|
||||
|
||||
const stoppedStatusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(stoppedStatusEvent.mode).toBe('stream');
|
||||
expect(stoppedStatusEvent.on).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks if the recording is started.
|
||||
* @param command
|
||||
*/
|
||||
async function testRecordingStarted(command: boolean) {
|
||||
const { p1, webhooksProxy } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
await p1.getIframeAPI().addEventListener('recordingStatusChanged');
|
||||
await p1.getIframeAPI().addEventListener('recordingLinkAvailable');
|
||||
|
||||
if (command) {
|
||||
await p1.getIframeAPI().executeCommand('startRecording', {
|
||||
mode: 'file'
|
||||
});
|
||||
} else {
|
||||
await p1.getIframeAPI().startRecording({
|
||||
mode: 'file'
|
||||
});
|
||||
}
|
||||
|
||||
if (webhooksProxy) {
|
||||
const recordingEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('RECORDING_STARTED');
|
||||
|
||||
expect('RECORDING_STARTED').toBe(recordingEvent.eventType);
|
||||
expect(recordingEvent.customerId).toBe(customerId);
|
||||
|
||||
webhooksProxy?.clearCache();
|
||||
}
|
||||
|
||||
const statusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(statusEvent.mode).toBe('file');
|
||||
expect(statusEvent.on).toBe(true);
|
||||
|
||||
const linkEvent = (await p1.getIframeAPI().getEventResult('recordingLinkAvailable'));
|
||||
|
||||
expect(linkEvent.link.startsWith('https://')).toBe(true);
|
||||
expect(linkEvent.link.includes(tenant)).toBe(true);
|
||||
expect(linkEvent.ttl > 0).toBe(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the recording is stopped.
|
||||
* @param command
|
||||
*/
|
||||
async function testRecordingStopped(command: boolean) {
|
||||
const { p1, webhooksProxy } = ctx;
|
||||
|
||||
await p1.switchToAPI();
|
||||
if (command) {
|
||||
await p1.getIframeAPI().executeCommand('stopRecording', 'file');
|
||||
} else {
|
||||
await p1.getIframeAPI().stopRecording('file');
|
||||
}
|
||||
|
||||
if (webhooksProxy) {
|
||||
const liveStreamEvent: {
|
||||
customerId: string;
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('RECORDING_ENDED');
|
||||
|
||||
expect('RECORDING_ENDED').toBe(liveStreamEvent.eventType);
|
||||
expect(liveStreamEvent.customerId).toBe(customerId);
|
||||
|
||||
const recordingUploadedEvent: {
|
||||
customerId: string;
|
||||
data: {
|
||||
initiatorId: string;
|
||||
participants: Array<string>;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('RECORDING_UPLOADED');
|
||||
|
||||
const jwtPayload = p1.getToken()?.payload;
|
||||
|
||||
expect(recordingUploadedEvent.data.initiatorId).toBe(jwtPayload?.context?.user?.id);
|
||||
expect(recordingUploadedEvent.data.participants.some(
|
||||
// @ts-ignore
|
||||
e => e.id === jwtPayload?.context?.user?.id)).toBe(true);
|
||||
|
||||
webhooksProxy?.clearCache();
|
||||
}
|
||||
|
||||
const statusEvent = (await p1.getIframeAPI().getEventResult('recordingStatusChanged'));
|
||||
|
||||
expect(statusEvent.mode).toBe('file');
|
||||
expect(statusEvent.on).toBe(false);
|
||||
|
||||
await p1.getIframeAPI().clearEventResults('recordingStatusChanged');
|
||||
}
|
||||
237
tests/specs/iframe/transcriptions.spec.ts
Normal file
237
tests/specs/iframe/transcriptions.spec.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
import { expect } from '@wdio/globals';
|
||||
|
||||
import type { Participant } from '../../helpers/Participant';
|
||||
import { setTestProperties } from '../../helpers/TestProperties';
|
||||
import type WebhookProxy from '../../helpers/WebhookProxy';
|
||||
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
setTestProperties(__filename, {
|
||||
useIFrameApi: true,
|
||||
useWebhookProxy: true,
|
||||
usesBrowsers: [ 'p1', 'p2' ]
|
||||
});
|
||||
|
||||
describe('Transcriptions', () => {
|
||||
it('joining the meeting', async () => {
|
||||
await ensureOneParticipant();
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
if (await p1.execute(() => config.disableIframeAPI || !config.transcription?.enabled)) {
|
||||
// skip the test if iframeAPI or transcriptions are disabled
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await p1.switchToAPI();
|
||||
|
||||
await ensureTwoParticipants({
|
||||
configOverwrite: {
|
||||
startWithAudioMuted: true
|
||||
}
|
||||
});
|
||||
|
||||
const { p2 } = ctx;
|
||||
|
||||
// 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 p1.getIframeAPI().getEventResult('videoConferenceJoined')).toBeDefined();
|
||||
});
|
||||
|
||||
it('toggle subtitles', async () => {
|
||||
const { p1, p2, webhooksProxy } = ctx;
|
||||
|
||||
await p1.getIframeAPI().addEventListener('transcriptionChunkReceived');
|
||||
await p2.getIframeAPI().addEventListener('transcriptionChunkReceived');
|
||||
await p1.getIframeAPI().executeCommand('toggleSubtitles');
|
||||
|
||||
await checkReceivingChunks(p1, p2, webhooksProxy);
|
||||
|
||||
await p1.getIframeAPI().executeCommand('toggleSubtitles');
|
||||
|
||||
// give it some time to process
|
||||
await p1.driver.pause(5000);
|
||||
});
|
||||
|
||||
it('set subtitles on and off', async () => {
|
||||
const { p1, p2, webhooksProxy } = ctx;
|
||||
|
||||
// we need to clear results or the last one will be used, form the previous time subtitles were on
|
||||
await p1.getIframeAPI().clearEventResults('transcriptionChunkReceived');
|
||||
await p2.getIframeAPI().clearEventResults('transcriptionChunkReceived');
|
||||
|
||||
await p1.getIframeAPI().executeCommand('setSubtitles', true, true);
|
||||
|
||||
await checkReceivingChunks(p1, p2, webhooksProxy);
|
||||
|
||||
await p1.getIframeAPI().executeCommand('setSubtitles', false);
|
||||
|
||||
// give it some time to process
|
||||
await p1.driver.pause(5000);
|
||||
});
|
||||
|
||||
it('start/stop transcriptions via recording', async () => {
|
||||
const { p1, p2, webhooksProxy } = ctx;
|
||||
|
||||
// we need to clear results or the last one will be used, form the previous time subtitles were on
|
||||
await p1.getIframeAPI().clearEventResults('transcriptionChunkReceived');
|
||||
await p2.getIframeAPI().clearEventResults('transcriptionChunkReceived');
|
||||
|
||||
await p1.getIframeAPI().addEventListener('transcribingStatusChanged');
|
||||
await p2.getIframeAPI().addEventListener('transcribingStatusChanged');
|
||||
|
||||
await p1.getIframeAPI().executeCommand('startRecording', { transcription: true });
|
||||
|
||||
let allTranscriptionStatusChanged: Promise<any>[] = [];
|
||||
|
||||
allTranscriptionStatusChanged.push(await p1.driver.waitUntil(() => p1.getIframeAPI()
|
||||
.getEventResult('transcribingStatusChanged'), {
|
||||
timeout: 10000,
|
||||
timeoutMsg: 'transcribingStatusChanged event not received on p1 side'
|
||||
}));
|
||||
allTranscriptionStatusChanged.push(await p2.driver.waitUntil(() => p2.getIframeAPI()
|
||||
.getEventResult('transcribingStatusChanged'), {
|
||||
timeout: 10000,
|
||||
timeoutMsg: 'transcribingStatusChanged event not received on p2 side'
|
||||
}));
|
||||
|
||||
let result = await Promise.allSettled(allTranscriptionStatusChanged);
|
||||
|
||||
expect(result.length).toBe(2);
|
||||
|
||||
result.forEach(e => {
|
||||
// @ts-ignore
|
||||
expect(e.value.on).toBe(true);
|
||||
});
|
||||
|
||||
await checkReceivingChunks(p1, p2, webhooksProxy);
|
||||
|
||||
await p1.getIframeAPI().clearEventResults('transcribingStatusChanged');
|
||||
await p2.getIframeAPI().clearEventResults('transcribingStatusChanged');
|
||||
|
||||
await p1.getIframeAPI().executeCommand('stopRecording', 'file', true);
|
||||
|
||||
allTranscriptionStatusChanged = [];
|
||||
|
||||
allTranscriptionStatusChanged.push(await p1.driver.waitUntil(() => p1.getIframeAPI()
|
||||
.getEventResult('transcribingStatusChanged'), {
|
||||
timeout: 10000,
|
||||
timeoutMsg: 'transcribingStatusChanged event not received on p1 side'
|
||||
}));
|
||||
allTranscriptionStatusChanged.push(await p2.driver.waitUntil(() => p2.getIframeAPI()
|
||||
.getEventResult('transcribingStatusChanged'), {
|
||||
timeout: 10000,
|
||||
timeoutMsg: 'transcribingStatusChanged event not received on p2 side'
|
||||
}));
|
||||
|
||||
result = await Promise.allSettled(allTranscriptionStatusChanged);
|
||||
|
||||
expect(result.length).toBe(2);
|
||||
|
||||
result.forEach(e => {
|
||||
// @ts-ignore
|
||||
expect(e.value.on).toBe(false);
|
||||
});
|
||||
|
||||
await p1.getIframeAPI().executeCommand('hangup');
|
||||
await p2.getIframeAPI().executeCommand('hangup');
|
||||
|
||||
// sometimes events are not immediately received,
|
||||
// let's wait for destroy event before waiting for those that depends on it
|
||||
await webhooksProxy.waitForEvent('ROOM_DESTROYED');
|
||||
|
||||
if (webhooksProxy) {
|
||||
const event: {
|
||||
data: {
|
||||
preAuthenticatedLink: string;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('TRANSCRIPTION_UPLOADED');
|
||||
|
||||
expect('TRANSCRIPTION_UPLOADED').toBe(event.eventType);
|
||||
expect(event.data.preAuthenticatedLink).toBeDefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
async function checkReceivingChunks(p1: Participant, p2: Participant, webhooksProxy: WebhookProxy) {
|
||||
const allTranscripts: Promise<any>[] = [];
|
||||
|
||||
allTranscripts.push(await p1.driver.waitUntil(() => p1.getIframeAPI()
|
||||
.getEventResult('transcriptionChunkReceived'), {
|
||||
timeout: 60000,
|
||||
timeoutMsg: 'transcriptionChunkReceived event not received on p1 side'
|
||||
}));
|
||||
|
||||
allTranscripts.push(await p2.driver.waitUntil(() => p2.getIframeAPI()
|
||||
.getEventResult('transcriptionChunkReceived'), {
|
||||
timeout: 60000,
|
||||
timeoutMsg: 'transcriptionChunkReceived event not received on p2 side'
|
||||
}));
|
||||
|
||||
if (webhooksProxy) {
|
||||
// TRANSCRIPTION_CHUNK_RECEIVED webhook
|
||||
allTranscripts.push((async () => {
|
||||
const event: {
|
||||
data: {
|
||||
final: string;
|
||||
language: string;
|
||||
messageID: string;
|
||||
participant: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
stable: string;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('TRANSCRIPTION_CHUNK_RECEIVED');
|
||||
|
||||
expect('TRANSCRIPTION_CHUNK_RECEIVED').toBe(event.eventType);
|
||||
|
||||
event.data.stable = event.data.final;
|
||||
|
||||
return event;
|
||||
})());
|
||||
}
|
||||
|
||||
const result = await Promise.allSettled(allTranscripts);
|
||||
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
|
||||
// @ts-ignore
|
||||
const firstEntryData = result[0].value.data;
|
||||
const stable = firstEntryData.stable || firstEntryData.final;
|
||||
const language = firstEntryData.language;
|
||||
const messageID = firstEntryData.messageID;
|
||||
const p1Id = await p1.getEndpointId();
|
||||
|
||||
result.map(r => {
|
||||
// @ts-ignore
|
||||
const v = r.value;
|
||||
|
||||
expect(v).toBeDefined();
|
||||
|
||||
return v.data;
|
||||
}).forEach(tr => {
|
||||
const checkTranscripts = stable.includes(tr.stable || tr.final) || (tr.stable || tr.final).includes(stable);
|
||||
|
||||
if (!checkTranscripts) {
|
||||
console.log('received events', JSON.stringify(result));
|
||||
}
|
||||
|
||||
expect(checkTranscripts).toBe(true);
|
||||
expect(tr.language).toBe(language);
|
||||
expect(tr.messageID).toBe(messageID);
|
||||
expect(tr.participant.id).toBe(p1Id);
|
||||
expect(tr.participant.name).toBe(p1.name);
|
||||
});
|
||||
}
|
||||
125
tests/specs/iframe/visitors.spec.ts
Normal file
125
tests/specs/iframe/visitors.spec.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { setTestProperties } from '../../helpers/TestProperties';
|
||||
import { config as testsConfig } from '../../helpers/TestsConfig';
|
||||
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
setTestProperties(__filename, {
|
||||
useIFrameApi: true,
|
||||
useWebhookProxy: true,
|
||||
usesBrowsers: [ 'p1', 'p2' ]
|
||||
});
|
||||
|
||||
describe('Visitors', () => {
|
||||
it('joining the meeting', async () => {
|
||||
const { webhooksProxy } = ctx;
|
||||
|
||||
if (webhooksProxy) {
|
||||
webhooksProxy.defaultMeetingSettings = {
|
||||
visitorsEnabled: true
|
||||
};
|
||||
}
|
||||
|
||||
await ensureOneParticipant();
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
if (await p1.execute(() => config.disableIframeAPI)) {
|
||||
// skip the test if iframeAPI is disabled or visitors are not supported
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await p1.driver.waitUntil(() => p1.execute(() => APP.conference._room.isVisitorsSupported()), {
|
||||
timeout: 2000
|
||||
}).then(async () => {
|
||||
await p1.switchToAPI();
|
||||
}).catch(() => {
|
||||
ctx.skipSuiteTests = true;
|
||||
});
|
||||
});
|
||||
|
||||
it('visitor joins', async () => {
|
||||
await ensureTwoParticipants({
|
||||
preferGenerateToken: true,
|
||||
tokenOptions: { visitor: true },
|
||||
skipInMeetingChecks: true
|
||||
});
|
||||
|
||||
const { p1, p2, webhooksProxy } = ctx;
|
||||
|
||||
await p2.waitForReceiveMedia(15_000, 'Visitor is not receiving media');
|
||||
await p2.waitForRemoteStreams(1);
|
||||
|
||||
const p2Visitors = p2.getVisitors();
|
||||
const p1Visitors = p1.getVisitors();
|
||||
|
||||
await p2.driver.waitUntil(() => p2Visitors.hasVisitorsDialog(), {
|
||||
timeout: 5000,
|
||||
timeoutMsg: 'Missing visitors dialog'
|
||||
});
|
||||
|
||||
expect((await p1Visitors.getVisitorsCount()).trim()).toBe('1');
|
||||
expect((await p1Visitors.getVisitorsHeaderFromParticipantsPane()).trim()).toBe('Viewers 1');
|
||||
|
||||
if (webhooksProxy) {
|
||||
// PARTICIPANT_JOINED webhook
|
||||
// @ts-ignore
|
||||
const event: {
|
||||
customerId: string;
|
||||
data: {
|
||||
avatar: string;
|
||||
email: string;
|
||||
group: string;
|
||||
id: string;
|
||||
name: string;
|
||||
participantJid: string;
|
||||
role: string;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('PARTICIPANT_JOINED');
|
||||
|
||||
const jwtPayload = p2.getToken()?.payload;
|
||||
|
||||
expect('PARTICIPANT_JOINED').toBe(event.eventType);
|
||||
expect(event.data.avatar).toBe(jwtPayload.context.user.avatar);
|
||||
expect(event.data.email).toBe(jwtPayload.context.user.email);
|
||||
expect(event.data.id).toBe(jwtPayload.context.user.id);
|
||||
expect(event.data.group).toBe(jwtPayload.context.group);
|
||||
expect(event.data.name).toBe(p2.name);
|
||||
expect(event.data.participantJid.indexOf('meet.jitsi') != -1).toBe(true);
|
||||
expect(event.data.name).toBe(p2.name);
|
||||
expect(event.data.role).toBe('visitor');
|
||||
expect(event.customerId).toBe(testsConfig.iframe.customerId);
|
||||
|
||||
await p2.switchToAPI();
|
||||
await p2.getIframeAPI().executeCommand('hangup');
|
||||
|
||||
// PARTICIPANT_LEFT webhook
|
||||
// @ts-ignore
|
||||
const eventLeft: {
|
||||
customerId: string;
|
||||
data: {
|
||||
avatar: string;
|
||||
email: string;
|
||||
group: string;
|
||||
id: string;
|
||||
name: string;
|
||||
participantJid: string;
|
||||
role: string;
|
||||
};
|
||||
eventType: string;
|
||||
} = await webhooksProxy.waitForEvent('PARTICIPANT_LEFT');
|
||||
|
||||
expect('PARTICIPANT_LEFT').toBe(eventLeft.eventType);
|
||||
expect(eventLeft.data.avatar).toBe(jwtPayload.context.user.avatar);
|
||||
expect(eventLeft.data.email).toBe(jwtPayload.context.user.email);
|
||||
expect(eventLeft.data.id).toBe(jwtPayload.context.user.id);
|
||||
expect(eventLeft.data.group).toBe(jwtPayload.context.group);
|
||||
expect(eventLeft.data.name).toBe(p2.name);
|
||||
expect(eventLeft.data.participantJid.indexOf('meet.jitsi') != -1).toBe(true);
|
||||
expect(eventLeft.data.name).toBe(p2.name);
|
||||
expect(eventLeft.data.role).toBe('visitor');
|
||||
expect(eventLeft.customerId).toBe(testsConfig.iframe.customerId);
|
||||
}
|
||||
});
|
||||
});
|
||||
81
tests/specs/iframe/visitorsLive.spec.ts
Normal file
81
tests/specs/iframe/visitorsLive.spec.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { expect } from '@wdio/globals';
|
||||
|
||||
import { setTestProperties } from '../../helpers/TestProperties';
|
||||
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
setTestProperties(__filename, {
|
||||
useIFrameApi: true,
|
||||
useWebhookProxy: true,
|
||||
usesBrowsers: [ 'p1', 'p2' ]
|
||||
});
|
||||
|
||||
describe('Visitors', () => {
|
||||
it('joining the meeting', async () => {
|
||||
const { webhooksProxy } = ctx;
|
||||
|
||||
if (webhooksProxy) {
|
||||
webhooksProxy.defaultMeetingSettings = {
|
||||
visitorsEnabled: true,
|
||||
visitorsLive: false
|
||||
};
|
||||
}
|
||||
|
||||
await ensureOneParticipant();
|
||||
|
||||
const { p1 } = ctx;
|
||||
|
||||
if (await p1.execute(() => config.disableIframeAPI)) {
|
||||
// skip the test if iframeAPI is disabled or visitors are not supported
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await p1.driver.waitUntil(() => p1.execute(() => APP.conference._room.isVisitorsSupported()), {
|
||||
timeout: 2000
|
||||
}).then(async () => {
|
||||
await p1.switchToAPI();
|
||||
}).catch(() => {
|
||||
ctx.skipSuiteTests = true;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('go live', async () => {
|
||||
await ensureTwoParticipants({
|
||||
preferGenerateToken: true,
|
||||
tokenOptions: { visitor: true },
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: true
|
||||
});
|
||||
|
||||
const { p1, p2 } = ctx;
|
||||
const p2Visitors = p2.getVisitors();
|
||||
const p1Visitors = p1.getVisitors();
|
||||
|
||||
await p2.driver.waitUntil(async () => p2Visitors.isVisitorsQueueUIShown(), {
|
||||
timeout: 5000,
|
||||
timeoutMsg: 'Missing visitors queue UI'
|
||||
});
|
||||
|
||||
await p1.driver.waitUntil(async () => await p1Visitors.getWaitingVisitorsInQueue()
|
||||
=== 'Viewers waiting in queue: 1', {
|
||||
timeout: 15000,
|
||||
timeoutMsg: 'Missing visitors queue count in UI'
|
||||
});
|
||||
|
||||
await p1Visitors.goLive();
|
||||
|
||||
await p2.waitToJoinMUC();
|
||||
await p2.waitForReceiveMedia(15000, 'Visitor is not receiving media');
|
||||
await p2.waitForRemoteStreams(1);
|
||||
|
||||
await p2.driver.waitUntil(() => p2Visitors.hasVisitorsDialog(), {
|
||||
timeout: 5000,
|
||||
timeoutMsg: 'Missing visitors dialog'
|
||||
});
|
||||
|
||||
expect((await p1Visitors.getVisitorsCount()).trim()).toBe('1');
|
||||
expect((await p1Visitors.getVisitorsHeaderFromParticipantsPane()).trim()).toBe('Viewers 1');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user