debug: drop some tests.

This commit is contained in:
damencho
2025-11-12 08:30:49 -06:00
parent dad4fb9e06
commit 99a5d7eaa9
41 changed files with 0 additions and 5302 deletions

View File

@@ -1,86 +0,0 @@
import type { Participant } from '../../../helpers/Participant';
import { setTestProperties } from '../../../helpers/TestProperties';
import { config as testsConfig } from '../../../helpers/TestsConfig';
import WebhookProxy from '../../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
import { waitForMedia } from './util';
setTestProperties(__filename, {
requireWebhookProxy: true,
useJaas: true,
});
describe('SIP jibri invite', () => {
let p1: Participant, webhooksProxy: WebhookProxy;
const customerId = testsConfig.jaas.customerId || '';
const dialOutUrl = process.env.SIP_JIBRI_DIAL_OUT_URL || '';
it('setup', async () => {
const room = ctx.roomName;
if (true) {
// This is temporary until we figure out how to fix it and configure it properly.
ctx.skipSuiteTests = 'This test is disabled as the code doesn\'t work anymore.';
return;
}
if (!dialOutUrl) {
ctx.skipSuiteTests = 'SIP_JIBRI_DIAL_OUT_URL is not set.';
return;
}
p1 = await joinJaasMuc({ name: 'p1', iFrameApi: true, token: t({ room, moderator: true }) });
webhooksProxy = ctx.webhooksProxy;
expect(await p1.isInMuc()).toBe(true);
expect(Boolean(await p1.execute(() => config.inviteServiceUrl))).toBe(true);
});
it('sip jibri', async () => {
await p1.switchToMainFrame();
await p1.getIframeAPI().inviteSIP(dialOutUrl);
await p1.switchToIFrame();
await p1.waitForParticipants(1);
await waitForMedia(p1);
const startedEvent: {
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(startedEvent.eventType);
expect(startedEvent.data.sipAddress).toBe(`sip:${process.env.SIP_JIBRI_DIAL_OUT_URL}`);
expect(startedEvent.customerId).toBe(customerId);
const endpointId = await p1.execute(() => APP?.conference?.listMembers()[0].getId());
await p1.getFilmstrip().kickParticipant(endpointId);
const endedEvent: {
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(endedEvent.eventType);
expect(endedEvent.customerId).toBe(customerId);
expect(endedEvent.data.participantFullJid).toBe(startedEvent.data.participantFullJid);
expect(endedEvent.data.participantId).toBe(startedEvent.data.participantId);
expect(endedEvent.data.participantJid).toBe(startedEvent.data.participantJid);
});
});

View File

@@ -1,25 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
setTestProperties(__filename, {
requireWebhookProxy: true,
useJaas: true
});
// This test is separate from passcode.spec.ts, because it needs to use a different room name, and webhooksProxy is only
// setup for the default room name.
describe('Setting invalid passcode through settings provisioning', () => {
it('With an invalid passcode', async () => {
ctx.webhooksProxy.defaultMeetingSettings = {
passcode: 'passcode-must-be-digits-only'
};
const p = await joinJaasMuc({ token: t({ room: ctx.roomName }) }, { roomName: ctx.roomName });
// The settings provisioning contains an invalid passcode, the expected result is that the room is not
// configured to require a passcode.
await p.waitToJoinMUC();
expect(await p.isInMuc()).toBe(true);
expect(await p.getPasswordDialog().isOpen()).toBe(false);
});
});

View File

@@ -1,178 +0,0 @@
import { expect } from '@wdio/globals';
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import WebhookProxy from '../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
setTestProperties(__filename, {
requireWebhookProxy: true,
useJaas: true,
usesBrowsers: [ 'p1', 'p2' ]
});
/**
* Tests the basic webhooks fired for participants joining, leaving, and creating/destroying a conference:
* PARTICIPANT_JOINED, PARTICIPANT_LEFT, ROOM_CREATED, ROOM_DESTROYED, ROLE_CHANGED, USAGE.
*/
describe('Create/destroy/join/leave webhooks', () => {
let conferenceJid: string = '';
let p1: Participant, p2: Participant;
let p1EpId: string, p2EpId: string;
let webhooksProxy: WebhookProxy;
let room: string;
async function checkParticipantJoinedHook(p: Participant) {
const event: {
data: {
conference: string;
isBreakout: boolean;
moderator: boolean;
name: string;
participantId: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('PARTICIPANT_JOINED');
expect(event.eventType).toBe('PARTICIPANT_JOINED');
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.isBreakout).toBe(false);
expect(event.data.moderator).toBe(p.getToken()?.options?.moderator);
expect(event.data.name).toBe(await p.getLocalDisplayName());
expect(event.data.participantId).toBe(await p.getEndpointId());
expect(event.data.name).toBe(p.name);
}
async function checkParticipantLeftHook(p: Participant, reason: string) {
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(event.eventType).toBe('PARTICIPANT_LEFT');
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);
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.jaas.customerId);
}
it('setup', async () => {
room = ctx.roomName;
conferenceJid = `${room}@conference.${testsConfig.jaas.tenant}.${new URL(process.env.BASE_URL || '').hostname}`;
webhooksProxy = ctx.webhooksProxy;
p1 = await joinJaasMuc({ name: 'p1', iFrameApi: true, token: t({ room, moderator: true }) });
p1EpId = await p1.getEndpointId();
expect(await p1.isModerator()).toBe(true);
await checkParticipantJoinedHook(p1);
await p1.switchToMainFrame();
p2 = await joinJaasMuc({ name: 'p2', token: t({ room }) });
p2EpId = await p2.getEndpointId();
expect(await p2.isModerator()).toBe(false);
await checkParticipantJoinedHook(p2);
});
it('USAGE webhook', async () => {
const event: {
data: [
{ participantId: string; }
];
eventType: string;
} = await webhooksProxy.waitForEvent('USAGE');
expect(event.eventType).toBe('USAGE');
expect(event.data.some(d => d.participantId === p1EpId));
expect(event.data.some(d => d.participantId === p2EpId));
});
it('ROOM_CREATED webhook', async () => {
const event: {
data: {
conference: string;
isBreakout: boolean;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROOM_CREATED');
expect(event.eventType).toBe('ROOM_CREATED');
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.isBreakout).toBe(false);
});
it('ROLE_CHANGED webhook', async () => {
await p1.getIframeAPI().executeCommand('grantModerator', p2EpId);
const event: {
data: {
grantedBy: {
participantId: string;
};
grantedTo: {
participantId: string;
};
role: string;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROLE_CHANGED');
expect(event.eventType).toBe('ROLE_CHANGED');
expect(event.data.role).toBe('moderator');
expect(event.data.grantedBy.participantId).toBe(p1EpId);
expect(event.data.grantedTo.participantId).toBe(p2EpId);
});
it('kick participant', async () => {
webhooksProxy.clearCache();
await p1.getIframeAPI().executeCommand('kickParticipant', p2EpId);
await checkParticipantLeftHook(p2, 'kicked');
});
it('join after kick', async () => {
webhooksProxy.clearCache();
// join again
p2 = await joinJaasMuc({ name: 'p2', token: t({ room }) });
p2EpId = await p2.getEndpointId();
await checkParticipantJoinedHook(p2);
});
it('hangup', async () => {
await p2.hangup();
await checkParticipantLeftHook(p2, 'left');
});
it('dispose conference', async () => {
await p1.getIframeAPI().executeCommand('hangup');
await checkParticipantLeftHook(p1, 'left');
const event: {
data: {
conference: string;
isBreakout: boolean;
};
eventType: string;
} = await webhooksProxy.waitForEvent('ROOM_DESTROYED');
expect(event.eventType).toBe('ROOM_DESTROYED');
expect(event.data.conference).toBe(conferenceJid);
expect(event.data.isBreakout).toBe(false);
});
});

View File

@@ -1,212 +0,0 @@
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import WebhookProxy from '../../helpers/WebhookProxy';
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: read flags from config.
* TODO: also assert "this meeting is being recorder" notificaitons are show/played?
*/
describe('Recording and live-streaming', () => {
const tenant = testsConfig.jaas.tenant;
const customerId = tenant?.replace('vpaas-magic-cookie-', '');
// TODO: read from config
let recordingDisabled: boolean;
// TODO: read from config
let liveStreamingDisabled: 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 });
// TODO: what should we do in this case? Add a config for this?
if (await p.execute(() => config.disableIframeAPI)) {
ctx.skipSuiteTests = 'The environment has the iFrame API disabled.';
return;
}
// TODO: only read if config says so
recordingDisabled = Boolean(!await p.execute(() => config.recordingService?.enabled));
liveStreamingDisabled = Boolean(!await p.execute(() => config.liveStreaming?.enabled))
|| !process.env.YTUBE_TEST_STREAM_KEY;
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<string>;
};
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 (recordingDisabled) {
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 (recordingDisabled) {
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 (liveStreamingDisabled) {
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);
});
});

View File

@@ -1,241 +0,0 @@
import { expect } from '@wdio/globals';
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import type WebhookProxy from '../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../helpers/jaas';
setTestProperties(__filename, {
requireWebhookProxy: true,
useJaas: true,
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Transcription', () => {
let p1: Participant, p2: Participant;
let webhooksProxy: WebhookProxy;
it('setup', async () => {
const room = ctx.roomName;
webhooksProxy = ctx.webhooksProxy;
p1 = await joinJaasMuc({
name: 'p1',
token: t({ room, moderator: true }),
iFrameApi: true });
if (await p1.execute(() => config.disableIframeAPI || !config.transcription?.enabled)) {
// skip the test if iframeAPI or transcriptions are disabled
ctx.skipSuiteTests = 'The environment has the iFrame API or transcriptions disabled.';
return;
}
p2 = await joinJaasMuc({
name: 'p2',
token: t({ room }),
iFrameApi: true }, {
configOverwrite: {
startWithAudioMuted: true
}
});
await Promise.all([
p1.switchToMainFrame(),
p2.switchToMainFrame(),
]);
expect(await p1.getIframeAPI().getEventResult('isModerator')).toBe(true);
expect(await p1.getIframeAPI().getEventResult('videoConferenceJoined')).toBeDefined();
});
it('toggle subtitles', async () => {
await p1.getIframeAPI().addEventListener('transcriptionChunkReceived');
await p2.getIframeAPI().addEventListener('transcriptionChunkReceived');
await p1.getIframeAPI().executeCommand('toggleSubtitles');
await checkReceivingChunks(p1, p2, webhooksProxy);
await p1.getIframeAPI().clearEventResults('transcribingStatusChanged');
await p1.getIframeAPI().addEventListener('transcribingStatusChanged');
await p1.getIframeAPI().executeCommand('toggleSubtitles');
await p1.driver.waitUntil(() => p1.getIframeAPI()
.getEventResult('transcribingStatusChanged'), {
timeout: 15000,
timeoutMsg: 'transcribingStatusChanged event not received by p1'
});
});
it('set subtitles on and off', async () => {
// we need to clear results or the last one will be used, from 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().clearEventResults('transcribingStatusChanged');
await p1.getIframeAPI().executeCommand('setSubtitles', false);
await p1.driver.waitUntil(() => p1.getIframeAPI()
.getEventResult('transcribingStatusChanged'), {
timeout: 15000,
timeoutMsg: 'transcribingStatusChanged event not received by p1'
});
});
it('start/stop transcriptions via recording', async () => {
// we need to clear results or the last one will be used, from the previous time subtitles were on
await p1.getIframeAPI().clearEventResults('transcribingStatusChanged');
await p1.getIframeAPI().clearEventResults('transcriptionChunkReceived');
await p2.getIframeAPI().clearEventResults('transcriptionChunkReceived');
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'
}));
allTranscriptionStatusChanged.push(await p2.driver.waitUntil(() => p2.getIframeAPI()
.getEventResult('transcribingStatusChanged'), {
timeout: 10000,
timeoutMsg: 'transcribingStatusChanged event not received on p2'
}));
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'
}));
allTranscriptionStatusChanged.push(await p2.driver.waitUntil(() => p2.getIframeAPI()
.getEventResult('transcribingStatusChanged'), {
timeout: 10000,
timeoutMsg: 'transcribingStatusChanged event not received on p2'
}));
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');
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'
}));
// 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);
});
}

View File

@@ -1,49 +0,0 @@
import { setTestProperties } from '../../../helpers/TestProperties';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
describe('Visitors triggered by reaching participantsSoftLimit', () => {
it('test participantsSoftLimit', async () => {
ctx.webhooksProxy.defaultMeetingSettings = {
participantsSoftLimit: 2,
visitorsEnabled: true
};
/// XXX the "name" of the participant MUST match one of the "capabilities" defined in wdio. It's not a "participant", it's an instance configuration!
const m = await joinJaasMuc({
token: t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
});
expect(await m.isInMuc()).toBe(true);
expect(await m.isModerator()).toBe(true);
expect(await m.isVisitor()).toBe(false);
console.log('Moderator joined');
// Joining with a participant token before participantSoftLimit has been reached
const p = await joinJaasMuc({
name: 'p2',
token: t({ room: ctx.roomName, displayName: 'Parti Cipant' })
});
expect(await p.isInMuc()).toBe(true);
expect(await p.isModerator()).toBe(false);
expect(await p.isVisitor()).toBe(false);
console.log('Participant joined');
// Joining with a participant token after participantSoftLimit has been reached
const v = await joinJaasMuc({
name: 'p3',
token: t({ room: ctx.roomName, displayName: 'Visi Tor' })
});
expect(await v.isInMuc()).toBe(true);
expect(await v.isModerator()).toBe(false);
expect(await v.isVisitor()).toBe(true);
console.log('Visitor joined');
});
});

View File

@@ -1,60 +0,0 @@
import { setTestProperties } from '../../../helpers/TestProperties';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
setTestProperties(__filename, {
requireWebhookProxy: true,
useJaas: true,
usesBrowsers: [ 'p1', 'p2', 'p3', 'p4' ]
});
/**
* This is a case which fails if jitsi-videobridge doesn't properly forward PLIs from visitors.
*/
describe('Visitor receiving video from a single remote participant', () => {
it('joining the meeting', async () => {
ctx.webhooksProxy.defaultMeetingSettings = {
visitorsEnabled: true,
visitorsLive: true,
};
// Force a connection via JVB.
const configOverwrite = {
p2p: {
enabled: false
}
};
const sender = await joinJaasMuc({
token: t({ room: ctx.roomName, displayName: 'Sender', moderator: true })
}, {
configOverwrite
});
const senderEndpointId = await sender.getEndpointId();
const testVisitor = async function(instanceId: 'p1' | 'p2' | 'p3' | 'p4') {
const visitor = await joinJaasMuc({
name: instanceId,
token: t({ room: ctx.roomName, displayName: 'Visitor', visitor: true })
}, {
configOverwrite
});
await visitor.waitForIceConnected();
const iceConnected = performance.now();
await visitor.driver.waitUntil(
() => visitor.isRemoteVideoReceivedAndDisplayed(senderEndpointId), {
timeout: 10_000,
timeoutMsg: `Visitor (${instanceId}) is not receiving video from the sender`
});
const duration = performance.now() - iceConnected;
console.log(`Video displayed after ${duration} ms after ICE connected (${instanceId})`);
};
await testVisitor('p2');
await testVisitor('p3');
await testVisitor('p4');
});
});

View File

@@ -1,140 +0,0 @@
import { Participant } from '../../../helpers/Participant';
import { setTestProperties } from '../../../helpers/TestProperties';
import { config as testsConfig } from '../../../helpers/TestsConfig';
import WebhookProxy from '../../../helpers/WebhookProxy';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
setTestProperties(__filename, {
requireWebhookProxy: true,
useJaas: true,
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
describe('Visitors triggered by visitor tokens', () => {
let webhooksProxy: WebhookProxy;
let room: string;
async function verifyJoinedWebhook(participant: Participant) {
const context = participant.getToken()?.payload.context;
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');
expect('PARTICIPANT_JOINED').toBe(event.eventType);
expect(event.data.avatar).toBe(context.user.avatar);
expect(event.data.email).toBe(context.user.email);
expect(event.data.id).toBe(context.user.id);
expect(event.data.group).toBe(context.group);
expect(event.data.name).toBe(context.user.name);
if (context.user.visitor) {
expect(event.data.participantJid.indexOf('meet.jitsi') != -1).toBe(true);
expect(event.data.role).toBe('visitor');
}
expect(event.customerId).toBe(testsConfig.jaas.customerId);
}
async function verifyLeftWebhook(participant: Participant) {
const context = participant.getToken()?.payload.context;
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(context.user.avatar);
expect(eventLeft.data.email).toBe(context.user.email);
expect(eventLeft.data.id).toBe(context.user.id);
expect(eventLeft.data.group).toBe(context.group);
expect(eventLeft.data.name).toBe(context.user.name);
if (context.user.visitor) {
expect(eventLeft.data.participantJid.indexOf('meet.jitsi') != -1).toBe(true);
expect(eventLeft.data.role).toBe('visitor');
}
expect(eventLeft.customerId).toBe(testsConfig.jaas.customerId);
}
it('setup', async () => {
webhooksProxy = ctx.webhooksProxy;
webhooksProxy.defaultMeetingSettings = {
visitorsEnabled: true
};
room = ctx.roomName;
});
it('test visitor tokens', async () => {
webhooksProxy.clearCache();
const moderatorToken = t({ room, displayName: 'Mo de Rator', moderator: true });
const moderator = await joinJaasMuc({ name: 'p1', token: moderatorToken });
expect(await moderator.isInMuc()).toBe(true);
expect(await moderator.isModerator()).toBe(true);
expect(await moderator.isVisitor()).toBe(false);
await verifyJoinedWebhook(moderator);
webhooksProxy.clearCache();
// Joining with a participant token before any visitors
const participantToken = t({ room, displayName: 'Parti Cipant' });
const participant = await joinJaasMuc({ name: 'p2', token: participantToken });
expect(await participant.isInMuc()).toBe(true);
expect(await participant.isModerator()).toBe(false);
expect(await participant.isVisitor()).toBe(false);
await verifyJoinedWebhook(participant);
webhooksProxy.clearCache();
// Joining with a visitor token
const visitorToken = t({ room, displayName: 'Visi Tor', visitor: true });
const visitor = await joinJaasMuc({ name: 'p3', token: visitorToken });
expect(await visitor.isInMuc()).toBe(true);
expect(await visitor.isModerator()).toBe(false);
expect(await visitor.isVisitor()).toBe(true);
await verifyJoinedWebhook(visitor);
webhooksProxy.clearCache();
await participant.hangup();
await verifyLeftWebhook(participant);
webhooksProxy.clearCache();
// Joining with a participant token after visitors -> visitor
const participantToken2 = t({ room, displayName: 'Visi Tor 2' });
const visitor2 = await joinJaasMuc({ name: 'p2', token: participantToken2 });
expect(await visitor2.isInMuc()).toBe(true);
expect(await visitor2.isModerator()).toBe(false);
expect(await visitor2.isVisitor()).toBe(true);
await verifyJoinedWebhook(visitor2);
webhooksProxy.clearCache();
await visitor.hangup();
await verifyLeftWebhook(visitor);
webhooksProxy.clearCache();
await visitor2.hangup();
await verifyLeftWebhook(visitor2);
webhooksProxy.clearCache();
await moderator.hangup();
await verifyLeftWebhook(moderator);
});
});

View File

@@ -1,73 +0,0 @@
import { expect } from '@wdio/globals';
import { Participant } from '../../../helpers/Participant';
import { setTestProperties } from '../../../helpers/TestProperties';
import { joinJaasMuc, generateJaasToken as t } from '../../../helpers/jaas';
setTestProperties(__filename, {
requireWebhookProxy: true,
useJaas: true,
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Visitors live', () => {
let visitor: Participant, moderator: Participant;
it('setup', async () => {
ctx.webhooksProxy.defaultMeetingSettings = {
visitorsEnabled: true,
visitorsLive: false
};
moderator = await joinJaasMuc({
name: 'p1',
token: t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
});
// TODO: Remove this in favor of configurable test expectations
await moderator.driver.waitUntil(() => moderator.execute(() => APP.conference._room.isVisitorsSupported()), {
timeout: 2000
}).catch(e => {
ctx.skipSuiteTests = `Because isVisitorsSupported() returned an error: ${e}.`;
});
visitor = await joinJaasMuc({
name: 'p2',
token: t({ room: ctx.roomName, displayName: 'Visi Tor', visitor: true })
}, {
skipWaitToJoin: true
});
});
it('go live', async () => {
const vVisitors = visitor.getVisitors();
const mVisitors = moderator.getVisitors();
await visitor.driver.waitUntil(async () => vVisitors.isVisitorsQueueUIShown(), {
timeout: 5000,
timeoutMsg: 'Missing visitors queue UI'
});
await moderator.driver.waitUntil(async () => await mVisitors.getWaitingVisitorsInQueue()
=== 'Viewers waiting in queue: 1', {
timeout: 15000,
timeoutMsg: 'Missing visitors queue count in UI'
});
await mVisitors.goLive();
await visitor.waitToJoinMUC();
await visitor.waitForReceiveMedia(15000, 'Visitor is not receiving media');
await visitor.waitForRemoteStreams(1);
await visitor.driver.waitUntil(() => vVisitors.hasVisitorsDialog(), {
timeout: 5000,
timeoutMsg: 'Missing visitors dialog'
});
expect((await mVisitors.getVisitorsCount()).trim()).toBe('1');
expect((await mVisitors.getVisitorsHeaderFromParticipantsPane()).trim()).toBe('Viewers 1');
});
});

View File

@@ -1,97 +0,0 @@
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureThreeParticipants } from '../../helpers/participants';
import { muteAudioAndCheck } from '../helpers/mute';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
describe('Active speaker', () => {
it('testActiveSpeaker', async () => {
await ensureThreeParticipants();
const { p1, p2, p3 } = ctx;
await muteAudioAndCheck(p1, p2);
await muteAudioAndCheck(p2, p1);
await muteAudioAndCheck(p3, p1);
// participant1 becomes active speaker - check from participant2's perspective
await testActiveSpeaker(p1, p2, p3);
// participant3 becomes active speaker - check from participant2's perspective
await testActiveSpeaker(p3, p2, p1);
// participant2 becomes active speaker - check from participant1's perspective
await testActiveSpeaker(p2, p1, p3);
// check the displayed speakers, there should be only one speaker
await assertOneDominantSpeaker(p1);
await assertOneDominantSpeaker(p2);
await assertOneDominantSpeaker(p3);
});
});
/**
* Tries to make given participant an active speaker by unmuting it.
* Verifies from {@code participant2}'s perspective that the active speaker
* has been displayed on the large video area. Mutes him back.
*
* @param {Participant} activeSpeaker - <tt>Participant</tt> instance of the participant who will be tested as an
* active speaker.
* @param {Participant} otherParticipant1 - <tt>Participant</tt> of the participant who will be observing and verifying
* active speaker change.
* @param {Participant} otherParticipant2 - Used only to print some debugging info.
*/
async function testActiveSpeaker(
activeSpeaker: Participant, otherParticipant1: Participant, otherParticipant2: Participant) {
await activeSpeaker.log(`Start testActiveSpeaker for participant: ${activeSpeaker.name}`);
const speakerEndpoint = await activeSpeaker.getEndpointId();
// just a debug print to go in logs
await activeSpeaker.log('Unmuting in testActiveSpeaker');
// Unmute
await activeSpeaker.getToolbar().clickAudioUnmuteButton();
// just a debug print to go in logs
await otherParticipant1.log(`Participant unmuted in testActiveSpeaker ${speakerEndpoint}`);
await otherParticipant2.log(`Participant unmuted in testActiveSpeaker ${speakerEndpoint}`);
await activeSpeaker.getFilmstrip().assertAudioMuteIconIsDisplayed(activeSpeaker, true);
// Verify that the user is now an active speaker from otherParticipant1's perspective
const otherParticipant1Driver = otherParticipant1.driver;
await otherParticipant1Driver.waitUntil(
async () => await otherParticipant1.getLargeVideo().getResource() === speakerEndpoint,
{
timeout: 30_000, // 30 seconds
timeoutMsg: 'Active speaker not displayed on large video.'
});
// just a debug print to go in logs
await activeSpeaker.log('Muting in testActiveSpeaker');
// Mute back again
await activeSpeaker.getToolbar().clickAudioMuteButton();
// just a debug print to go in logs
await otherParticipant1.log(`Participant muted in testActiveSpeaker ${speakerEndpoint}`);
await otherParticipant2.log(`Participant muted in testActiveSpeaker ${speakerEndpoint}`);
await otherParticipant1.getFilmstrip().assertAudioMuteIconIsDisplayed(activeSpeaker);
}
/**
* Asserts that the number of small videos with the dominant speaker
* indicator displayed equals 1.
*
* @param {Participant} participant - The participant to check.
*/
async function assertOneDominantSpeaker(participant: Participant) {
expect(await participant.driver.$$(
'//span[not(contains(@class, "tile-view"))]//span[contains(@class,"dominant-speaker")]').length).toBe(1);
}

View File

@@ -1,91 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Audio-only mode', () => {
it('joining the meeting', () => ensureTwoParticipants());
/**
* Enables audio only mode for p1 and verifies that the other participant sees participant1 as video muted.
*/
it('set and check', () => setAudioOnlyAndCheck(true));
/**
* Verifies that participant1 sees avatars for itself and other participants.
*/
it('avatars check', async () => {
const { p1 } = ctx;
await p1.driver.$('//div[@id="dominantSpeaker"]').waitForDisplayed();
// Makes sure that the avatar is displayed in the local thumbnail and that the video is not displayed.
await p1.assertThumbnailShowsAvatar(p1);
});
/**
* Disables audio only mode and verifies that both participants see p1 as not video muted.
*/
it('disable and check', () => setAudioOnlyAndCheck(false));
/**
* Mutes video on participant1, toggles audio-only twice and then verifies if both participants see participant1
* as video muted.
*/
it('mute video, set twice and check muted', async () => {
const { p1 } = ctx;
// Mute video on participant1.
await p1.getToolbar().clickVideoMuteButton();
await verifyVideoMute(true);
// Enable audio-only mode.
await setAudioOnlyAndCheck(true);
// Disable audio-only mode.
await p1.getVideoQualityDialog().setVideoQuality(false);
// p1 should stay muted since it was muted before audio-only was enabled.
await verifyVideoMute(true);
});
it('unmute video and check not muted', async () => {
// Unmute video on participant1.
await ctx.p1.getToolbar().clickVideoUnmuteButton();
await verifyVideoMute(false);
});
});
/**
* Toggles the audio only state of a p1 participant and verifies participant sees the audio only label and that
* p2 participant sees a video mute state for the former.
* @param enable
*/
async function setAudioOnlyAndCheck(enable: boolean) {
const { p1 } = ctx;
await p1.getVideoQualityDialog().setVideoQuality(enable);
await verifyVideoMute(enable);
await p1.driver.$('//div[@id="videoResolutionLabel"][contains(@class, "audio-only")]')
.waitForDisplayed({ reverse: !enable });
}
/**
* Verifies that p1 and p2 see p1 as video muted or not.
* @param muted
*/
async function verifyVideoMute(muted: boolean) {
const { p1, p2 } = ctx;
// Verify the observer sees the testee in the desired muted state.
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1, !muted);
// Verify the testee sees itself in the desired muted state.
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1, !muted);
}

View File

@@ -1,303 +0,0 @@
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { expectations } from '../../helpers/expectations';
import {
ensureOneParticipant,
ensureThreeParticipants, ensureTwoParticipants,
hangupAllParticipants
} from '../../helpers/participants';
import { unmuteAudioAndCheck, unmuteVideoAndCheck } from '../helpers/mute';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
describe('Audio/video moderation', () => {
it('setup', async () => {
await ensureThreeParticipants();
const { p1, p2, p3 } = ctx;
// if all 3 participants are moderators, skip this test
if (!await p1.isModerator()
|| (await p1.isModerator() && await p2.isModerator() && await p3.isModerator())) {
ctx.skipSuiteTests = `Unsupported moderator configuration: p1=${await p1.isModerator()},\
p2=${await p2.isModerator()}, p3=${await p3.isModerator()}`;
}
});
it('check audio enable/disable', async () => {
const { p1, p3 } = ctx;
const p1ParticipantsPane = p1.getParticipantsPane();
await p1ParticipantsPane.clickContextMenuButton();
await p1ParticipantsPane.getAVModerationMenu().clickStartAudioModeration();
await p1ParticipantsPane.close();
// Here we want to try unmuting and check that we are still muted.
await tryToAudioUnmuteAndCheck(p3, p1);
await p1ParticipantsPane.clickContextMenuButton();
await p1ParticipantsPane.getAVModerationMenu().clickStopAudioModeration();
await p1ParticipantsPane.close();
await unmuteAudioAndCheck(p3, p1);
});
it('check video enable/disable', async () => {
const { p1, p3 } = ctx;
const p1ParticipantsPane = p1.getParticipantsPane();
await p1ParticipantsPane.clickContextMenuButton();
await p1ParticipantsPane.getAVModerationMenu().clickStartVideoModeration();
await p1ParticipantsPane.close();
// Here we want to try unmuting and check that we are still muted.
await tryToVideoUnmuteAndCheck(p3, p1);
await p1ParticipantsPane.clickContextMenuButton();
await p1ParticipantsPane.getAVModerationMenu().clickStopVideoModeration();
await p1ParticipantsPane.close();
await unmuteVideoAndCheck(p3, p1);
});
it('unmute by moderator', async () => {
const { p1, p2, p3 } = ctx;
await unmuteByModerator(p1, p3, true, true);
// moderation is stopped at this point, make sure participants 1 & 2 are also unmuted,
// participant3 was unmuted by unmuteByModerator
await unmuteAudioAndCheck(p2, p1);
await unmuteVideoAndCheck(p2, p1);
// make sure p1 is not muted after turning on and then off the AV moderation
await p1.getFilmstrip().assertAudioMuteIconIsDisplayed(p1, true);
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p2, true);
});
it('hangup and change moderator', async () => {
// The test below is only correct when the environment is configured to automatically elect a new moderator
// when the moderator leaves. For environments where this is not the case, the test is skipped.
if (!expectations.autoModerator) {
return;
}
await Promise.all([ ctx.p2.hangup(), ctx.p3.hangup() ]);
await ensureThreeParticipants();
const { p1, p2, p3 } = ctx;
await p2.getToolbar().clickAudioMuteButton();
await p3.getToolbar().clickAudioMuteButton();
const p1ParticipantsPane = p1.getParticipantsPane();
await p1ParticipantsPane.clickContextMenuButton();
await p1ParticipantsPane.getAVModerationMenu().clickStartAudioModeration();
await p1ParticipantsPane.getAVModerationMenu().clickStartVideoModeration();
await p2.getToolbar().clickRaiseHandButton();
await p3.getToolbar().clickRaiseHandButton();
await p1.hangup();
// we don't use ensureThreeParticipants to avoid all meeting join checks
// all participants are muted and checks for media will fail
await ensureOneParticipant();
// After p1 re-joins either p2 or p3 is promoted to moderator. They should still be muted.
const isP2Moderator = await p2.isModerator();
const moderator = isP2Moderator ? p2 : p3;
const nonModerator = isP2Moderator ? p3 : p2;
const moderatorParticipantsPane = moderator.getParticipantsPane();
const nonModeratorParticipantsPane = nonModerator.getParticipantsPane();
await moderatorParticipantsPane.assertVideoMuteIconIsDisplayed(moderator);
await nonModeratorParticipantsPane.assertVideoMuteIconIsDisplayed(nonModerator);
await moderatorParticipantsPane.allowVideo(nonModerator);
await moderatorParticipantsPane.askToUnmute(nonModerator, false);
await nonModerator.getNotifications().waitForAskToUnmuteNotification();
await unmuteAudioAndCheck(nonModerator, p1);
await unmuteVideoAndCheck(nonModerator, p1);
await moderatorParticipantsPane.clickContextMenuButton();
await moderatorParticipantsPane.getAVModerationMenu().clickStopAudioModeration();
await moderatorParticipantsPane.getAVModerationMenu().clickStopVideoModeration();
});
it('grant moderator', async () => {
await hangupAllParticipants();
await ensureThreeParticipants();
const { p1, p2, p3 } = ctx;
const p1ParticipantsPane = p1.getParticipantsPane();
await p1ParticipantsPane.clickContextMenuButton();
await p1ParticipantsPane.getAVModerationMenu().clickStartAudioModeration();
await p1ParticipantsPane.getAVModerationMenu().clickStartVideoModeration();
await p1.getFilmstrip().grantModerator(p3);
await p3.driver.waitUntil(
() => p3.isModerator(), {
timeout: 5000,
timeoutMsg: `${p3.name} is not moderator`
});
await unmuteByModerator(p3, p2, false, true);
});
it('ask to unmute', async () => {
await hangupAllParticipants();
await ensureTwoParticipants();
const { p1, p2 } = ctx;
// mute p2
await p2.getToolbar().clickAudioMuteButton();
// ask p2 to unmute
await p1.getParticipantsPane().askToUnmute(p2, true);
await p2.getNotifications().waitForAskToUnmuteNotification();
await p1.getParticipantsPane().close();
});
it('remove from whitelist', async () => {
const { p1, p2 } = ctx;
await unmuteByModerator(p1, p2, true, false);
// p1 mute audio on p2 and check
await p1.getFilmstrip().muteAudio(p2);
await p1.getFilmstrip().assertAudioMuteIconIsDisplayed(p2);
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p2);
// we try to unmute and test it that it was still muted
await tryToAudioUnmuteAndCheck(p2, p1);
// stop video and check
await p1.getFilmstrip().muteVideo(p2);
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
await tryToVideoUnmuteAndCheck(p2, p1);
});
it('join moderated', async () => {
await hangupAllParticipants();
await ensureOneParticipant();
const p1ParticipantsPane = ctx.p1.getParticipantsPane();
await p1ParticipantsPane.clickContextMenuButton();
await p1ParticipantsPane.getAVModerationMenu().clickStartAudioModeration();
await p1ParticipantsPane.getAVModerationMenu().clickStartVideoModeration();
await p1ParticipantsPane.close();
// join with second participant and check
await ensureTwoParticipants({
skipInMeetingChecks: true
});
const { p1, p2 } = ctx;
await p2.getNotifications().closeYouAreMutedNotification();
await tryToAudioUnmuteAndCheck(p2, p1);
await tryToVideoUnmuteAndCheck(p2, p1);
// asked to unmute and check
await unmuteByModerator(p1, p2, false, false);
// mute and check
await p1.getFilmstrip().muteAudio(p2);
await p1.getFilmstrip().assertAudioMuteIconIsDisplayed(p2);
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p2);
await tryToAudioUnmuteAndCheck(p2, p1);
});
});
/**
* Checks a user can unmute after being asked by moderator.
* @param moderator - The participant that is moderator.
* @param participant - The participant being asked to unmute.
* @param turnOnModeration - if we want to turn on moderation before testing (when it is currently off).
* @param stopModeration - true if moderation to be stopped when done.
*/
async function unmuteByModerator(
moderator: Participant,
participant: Participant,
turnOnModeration: boolean,
stopModeration: boolean) {
const moderatorParticipantsPane = moderator.getParticipantsPane();
if (turnOnModeration) {
await moderatorParticipantsPane.clickContextMenuButton();
await moderatorParticipantsPane.getAVModerationMenu().clickStartAudioModeration();
await moderatorParticipantsPane.getAVModerationMenu().clickStartVideoModeration();
await moderatorParticipantsPane.close();
}
// raise hand to speak
await participant.getToolbar().clickRaiseHandButton();
await moderator.getNotifications().waitForRaisedHandNotification();
// ask participant to unmute
await moderatorParticipantsPane.allowVideo(participant);
await moderatorParticipantsPane.askToUnmute(participant, false);
await participant.getNotifications().waitForAskToUnmuteNotification();
await unmuteAudioAndCheck(participant, moderator);
await unmuteVideoAndCheck(participant, moderator);
if (stopModeration) {
await moderatorParticipantsPane.clickContextMenuButton();
await moderatorParticipantsPane.getAVModerationMenu().clickStopAudioModeration();
await moderatorParticipantsPane.getAVModerationMenu().clickStopVideoModeration();
await moderatorParticipantsPane.close();
}
}
/**
* In case of moderation, tries to audio unmute but stays muted.
* Checks locally and remotely that this is still the case.
* @param participant
* @param observer
*/
async function tryToAudioUnmuteAndCheck(participant: Participant, observer: Participant) {
// try to audio unmute and check
await participant.getToolbar().clickAudioUnmuteButton();
// Check local audio muted icon state
await participant.getFilmstrip().assertAudioMuteIconIsDisplayed(participant);
await observer.getFilmstrip().assertAudioMuteIconIsDisplayed(participant);
}
/**
* In case of moderation, tries to video unmute but stays muted.
* Checks locally and remotely that this is still the case.
* @param participant
* @param observer
*/
async function tryToVideoUnmuteAndCheck(participant: Participant, observer: Participant) {
// try to video unmute and check
await participant.getToolbar().clickVideoUnmuteButton();
// Check local audio muted icon state
await participant.getParticipantsPane().assertVideoMuteIconIsDisplayed(participant);
await observer.getParticipantsPane().assertVideoMuteIconIsDisplayed(participant);
}

View File

@@ -1,132 +0,0 @@
import { VIDEO_CODEC } from '../../../react/features/video-quality/constants';
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import {
ensureOneParticipant,
ensureThreeParticipants,
ensureTwoParticipants,
hangupAllParticipants
} from '../../helpers/participants';
const { VP8, VP9, AV1 } = VIDEO_CODEC;
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
describe('Codec selection', () => {
it('asymmetric codecs', async () => {
await ensureOneParticipant({
configOverwrite: {
videoQuality: {
codecPreferenceOrder: [ 'VP9', 'VP8', 'AV1' ]
}
}
});
await ensureTwoParticipants({
configOverwrite: {
videoQuality: {
codecPreferenceOrder: [ 'VP8', 'VP9', 'AV1' ]
}
}
});
const { p1, p2 } = ctx;
// Check if media is playing on both endpoints.
expect(await p1.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
expect(await p2.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
// Check if p1 is sending VP9 and p2 is sending VP8 as per their codec preferences.
// Except on Firefox because it doesn't support VP9 encode.
const p1ExpectedCodec = p1.driver.isFirefox ? VP8 : VP9;
await Promise.all([
waitForCodec(p1, p1ExpectedCodec),
waitForCodec(p2, VP8)
]);
});
it('asymmetric codecs with AV1', async () => {
await ensureThreeParticipants({
configOverwrite: {
videoQuality: {
codecPreferenceOrder: [ 'AV1', 'VP9', 'VP8' ]
}
}
});
const { p1, p2, p3 } = ctx;
// Check if media is playing on p3.
expect(await p3.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
const majorVersion = parseInt(p1.driver.capabilities.browserVersion || '0', 10);
// Check if p1 is encoding in VP9, p2 in VP8 and p3 in AV1 as per their codec preferences.
// Except on Firefox because it doesn't support VP9 encode.
const p1ExpectedCodec = p1.driver.isFirefox ? VP8 : VP9;
const p3ExpectedCodec = (p1.driver.isFirefox && majorVersion < 136) ? VP9 : AV1;
await Promise.all([
waitForCodec(p1, p1ExpectedCodec),
waitForCodec(p2, VP8),
waitForCodec(p3, p3ExpectedCodec)
]);
});
it('codec switch over', async () => {
await hangupAllParticipants();
await ensureTwoParticipants({
configOverwrite: {
videoQuality: {
codecPreferenceOrder: [ 'VP9', 'VP8', 'AV1' ]
}
}
});
const { p1, p2 } = ctx;
// Disable this test on Firefox because it doesn't support VP9 encode.
if (p1.driver.isFirefox) {
return;
}
// Check if p1 and p2 are encoding in VP9 which is the default codec.
await Promise.all([
waitForCodec(p1, VP9),
waitForCodec(p2, VP9)
]);
await ensureThreeParticipants({
configOverwrite: {
videoQuality: {
codecPreferenceOrder: [ 'VP8' ]
}
}
});
const { p3 } = ctx;
await Promise.all([
waitForCodec(p1, VP8),
waitForCodec(p2, VP8),
waitForCodec(p3, VP8)
]);
await p3.hangup();
// Check of p1 and p2 have switched to VP9.
await Promise.all([
waitForCodec(p1, VP9),
waitForCodec(p2, VP9)
]);
});
});
async function waitForCodec(p: Participant, codec: string) {
await p.driver.waitUntil(
() => p.execute(c => JitsiMeetJS.app.testing.getLocalCameraEncoding() === c, codec),
{
timeout: 10000,
timeoutMsg: `${p.name} failed to use VP8`
}
);
}

View File

@@ -1,386 +0,0 @@
import { SET_AUDIO_ONLY } from '../../../react/features/base/audio-only/actionTypes';
import { setTestProperties } from '../../helpers/TestProperties';
import {
checkForScreensharingTile,
ensureFourParticipants,
ensureOneParticipant,
ensureThreeParticipants,
ensureTwoParticipants,
hangupAllParticipants
} from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3', 'p4' ]
});
describe('Desktop sharing', () => {
it('start', async () => {
await ensureTwoParticipants({
configOverwrite: {
p2p: {
backToP2PDelay: 3,
enabled: true
}
}
});
const { p1, p2 } = ctx;
await p1.waitForP2PIceConnected();
await p2.getToolbar().clickDesktopSharingButton();
// Check if a remote screen share tile is created on p1.
await checkForScreensharingTile(p2, p1);
// Check if a local screen share tile is created on p2.
await checkForScreensharingTile(p2, p2);
expect(await p2.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
});
it('stop', async () => {
const { p1, p2 } = ctx;
await p2.getToolbar().clickStopDesktopSharingButton();
// Check if the local screen share thumbnail disappears on p2.
await checkForScreensharingTile(p2, p2, true);
// Check if the remote screen share thumbnail disappears on p1.
await checkForScreensharingTile(p1, p2, true);
});
/**
* Ensures screen share is still visible when the call switches from p2p to jvb connection.
*/
it('p2p to jvb switch', async () => {
await ctx.p2.getToolbar().clickDesktopSharingButton();
await ensureThreeParticipants({
configOverwrite: {
p2p: {
enabled: true
}
}
});
const { p1, p2, p3 } = ctx;
const p2EndpointId = await p2.getEndpointId();
// Check if a remote screen share tile is created on all participants.
await checkForScreensharingTile(p2, p1);
await checkForScreensharingTile(p2, p2);
await checkForScreensharingTile(p2, p3);
await p1.getFilmstrip().assertNoGapsInFilmstrip();
await p3.getFilmstrip().assertNoGapsInFilmstrip();
await p3.waitForParticipantOnLargeVideo(`${p2EndpointId}-v1`);
});
/**
* Ensure screen share is still visible when the call switches from jvb to p2p and back.
*/
it('p2p to jvb switch and back', async () => {
const { p1, p2, p3 } = ctx;
await p3.hangup();
// Wait for p1 and p2 to switch back to p2p.
await p1.waitForP2PIceConnected();
await p2.waitForP2PIceConnected();
// Check if a remote screen share tile is created on p1 and p2 after switching back to p2p.
await checkForScreensharingTile(p2, p1);
await checkForScreensharingTile(p2, p2);
// The video should be playing.
await p1.waitForParticipantOnLargeVideo(`${await p2.getEndpointId()}-v1`);
// Start desktop share on p1.
await p1.getToolbar().clickDesktopSharingButton();
// Check if a new tile for p1's screen share is created on both p1 and p2.
await checkForScreensharingTile(p1, p1);
await checkForScreensharingTile(p1, p2);
await ensureThreeParticipants({
configOverwrite: {
p2p: {
backToP2PDelay: 3,
enabled: true
}
}
});
await checkForScreensharingTile(p1, p3);
await checkForScreensharingTile(p2, p3);
});
/**
* Ensure that screen share is still visible in jvb connection when share is toggled while the users are
* in p2p mode, i.e., share is restarted when user is in p2p mode and then the call switches over to jvb mode.
*/
it('stop screen sharing and back', async () => {
const { p1, p2, p3 } = ctx;
// Stop share on both p1 and p2.
await p1.getToolbar().clickStopDesktopSharingButton();
await p2.getToolbar().clickStopDesktopSharingButton();
await p3.hangup();
// Wait for p1 and p2 to switch back to p2p.
await p1.waitForP2PIceConnected();
await p2.waitForP2PIceConnected();
// Start share on both p1 and p2.
await p1.getToolbar().clickDesktopSharingButton();
await p2.getToolbar().clickDesktopSharingButton();
// Check if p1 and p2 can see each other's shares in p2p.
await checkForScreensharingTile(p1, p2);
await checkForScreensharingTile(p2, p1);
// Add p3 back to the conference and check if p1 and p2's shares are visible on p3.
await ensureThreeParticipants({
configOverwrite: {
p2p: {
backToP2PDelay: 3,
enabled: true
}
}
});
await checkForScreensharingTile(p1, p3);
await checkForScreensharingTile(p2, p3);
// Add another particpant to verify multiple screenshares are visible without gaps in filmstrip.
await ensureFourParticipants({
configOverwrite: {
filmstrip: {
stageFilmstripParticipants: 2
},
startWithAudioMuted: true
}
});
const { p4 } = ctx;
await checkForScreensharingTile(p1, p4);
await checkForScreensharingTile(p2, p4);
await p3.getFilmstrip().assertNoGapsInFilmstrip();
await p4.getFilmstrip().assertNoGapsInFilmstrip();
});
/**
* Ensures screen share is visible when a muted screen share track is added to the conference, i.e.,
* users starts and stops the share before anyone else joins the call.
* The call switches to jvb and then back to p2p.
*/
it('screen sharing toggle before others join', async () => {
await hangupAllParticipants();
await ensureOneParticipant({
configOverwrite: {
p2p: {
backToP2PDelay: 3,
enabled: true
}
}
});
const { p1 } = ctx;
// p1 starts share when alone in the call.
await p1.getToolbar().clickDesktopSharingButton();
await checkForScreensharingTile(p1, p1);
// p1 stops share.
await p1.getToolbar().clickStopDesktopSharingButton();
// Call switches to jvb.
await ensureThreeParticipants({
configOverwrite: {
p2p: {
backToP2PDelay: 3,
enabled: true
}
}
});
const { p2, p3 } = ctx;
const p1EndpointId = await p1.getEndpointId();
const p1ScreenShareTileId = `${p1EndpointId}-v1`;
const p2EndpointId = await p2.getEndpointId();
const p2ScreenShareTileId = `${p2EndpointId}-v1`;
// p1 starts share again when call switches to jvb.
await p1.getToolbar().clickDesktopSharingButton();
// Check p2 and p3 are able to see p1's share.
await checkForScreensharingTile(p1, p2);
await checkForScreensharingTile(p1, p3);
await p2.waitForParticipantOnLargeVideo(p1ScreenShareTileId);
await p3.waitForParticipantOnLargeVideo(p1ScreenShareTileId);
// p3 leaves the call.
await p3.hangup();
// Wait for p1 and p2 to switch back to p2p.
await p1.waitForP2PIceConnected();
await p2.waitForP2PIceConnected();
// Make sure p2 see's p1's share after the call switches back to p2p.
await checkForScreensharingTile(p1, p2);
await p2.waitForParticipantOnLargeVideo(p1ScreenShareTileId);
// p2 starts share when in p2p.
await p2.getToolbar().clickDesktopSharingButton();
// Makes sure p2's share is visible on p1.
await checkForScreensharingTile(p2, p1);
await p1.waitForParticipantOnLargeVideo(p2ScreenShareTileId);
});
/**
* A case where a non-dominant speaker is sharing screen for a participant in low bandwidth mode
* where only a screen share can be received. A bug fixed in jvb 0c5dd91b where the video was not received.
*/
it('audio only and non dominant screen share', async () => {
await hangupAllParticipants();
await ensureOneParticipant();
const { p1 } = ctx;
// a workaround to directly set audio only mode without going through the rest of the settings in the UI
await p1.execute(type => {
APP?.store?.dispatch({
type,
audioOnly: true
});
APP?.conference?.onToggleAudioOnly();
}, SET_AUDIO_ONLY);
await p1.getToolbar().clickAudioMuteButton();
await ensureThreeParticipants({
skipInMeetingChecks: true
});
const { p2, p3 } = ctx;
await p3.getToolbar().clickAudioMuteButton();
await p3.getToolbar().clickDesktopSharingButton();
await checkForScreensharingTile(p3, p1);
await checkForScreensharingTile(p3, p2);
// the video should be playing
await p1.driver.waitUntil(() => p1.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived()), {
timeout: 5_000,
timeoutMsg: 'expected remote screen share to be on large'
});
});
/**
* A case where first participant is muted (a&v) and enters low bandwidth mode,
* the second one is audio muted only and the one sharing (the third) is dominant speaker.
* A problem fixed in jitsi-meet 3657c19e and d6ab0a72.
*/
it('audio only and dominant screen share', async () => {
await hangupAllParticipants();
await ensureOneParticipant({
configOverwrite: {
startWithAudioMuted: true,
startWithVideoMuted: true
}
});
const { p1 } = ctx;
// a workaround to directly set audio only mode without going through the rest of the settings in the UI
await p1.execute(type => {
APP?.store?.dispatch({
type,
audioOnly: true
});
APP?.conference?.onToggleAudioOnly();
}, SET_AUDIO_ONLY);
await ensureTwoParticipants({
configOverwrite: {
startWithAudioMuted: true
},
skipInMeetingChecks: true
});
await ensureThreeParticipants({
skipInMeetingChecks: true
});
const { p2, p3 } = ctx;
await p3.getToolbar().clickDesktopSharingButton();
await checkForScreensharingTile(p3, p1);
await checkForScreensharingTile(p3, p2);
// The desktop sharing participant should be on large
expect(await p1.getLargeVideo().getResource()).toBe(`${await p3.getEndpointId()}-v1`);
// the video should be playing
await p1.driver.waitUntil(() => p1.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived()), {
timeout: 5_000,
timeoutMsg: 'expected remote screen share to be on large'
});
});
/**
* Test screensharing with lastN. We add p4 with lastN=2 and verify that it receives the expected streams.
*/
it('with lastN', async () => {
await hangupAllParticipants();
await ensureThreeParticipants();
const { p1, p2, p3 } = ctx;
await p3.getToolbar().clickDesktopSharingButton();
await p1.getToolbar().clickAudioMuteButton();
await p3.getToolbar().clickAudioMuteButton();
await ensureFourParticipants({
configOverwrite: {
channelLastN: 2,
startWithAudioMuted: true
}
});
const { p4 } = ctx;
// We now have p1, p2, p3, p4.
// p3 is screensharing.
// p1, p3, p4 are audio muted, so p2 should eventually become dominant speaker.
// Participants should display p3 on-stage because it is screensharing.
await checkForScreensharingTile(p3, p1);
await checkForScreensharingTile(p3, p2);
await checkForScreensharingTile(p3, p4);
// And the video should be playing
expect(await p4.execute(() => JitsiMeetJS.app.testing.isLargeVideoReceived())).toBe(true);
// Check that there are no gaps in the filmstrip when participant joins during screensharing
await p1.getFilmstrip().assertNoGapsInFilmstrip();
await p2.getFilmstrip().assertNoGapsInFilmstrip();
await p4.getFilmstrip().assertNoGapsInFilmstrip();
const p1EndpointId = await p1.getEndpointId();
const p2EndpointId = await p2.getEndpointId();
// p4 has lastN=2 and has selected p3. With p2 being dominant speaker p4 should eventually
// see video for [p3, p2] and p1 as ninja.
await p4.waitForNinjaIcon(p1EndpointId);
await p4.waitForRemoteVideo(p2EndpointId);
// Let's switch and check, muting participant 2 and unmuting 1 will leave participant 1 as dominant
await p1.getToolbar().clickAudioUnmuteButton();
await p2.getToolbar().clickAudioMuteButton();
// Participant4 should eventually see video for [p3, p1] and p2 as a ninja.
await p4.waitForNinjaIcon(p2EndpointId);
await p4.waitForRemoteVideo(p1EndpointId);
});
});

View File

@@ -1,78 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureFourParticipants, ensureThreeParticipants, ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3', 'p4' ]
});
describe('LastN', () => {
it('joining the meeting', async () => {
await ensureTwoParticipants({
configOverwrite: {
startWithAudioMuted: true,
startWithVideoMuted: true,
channelLastN: 1
},
skipInMeetingChecks: true
});
await ensureThreeParticipants({
configOverwrite: {
channelLastN: 1
},
skipInMeetingChecks: true,
});
});
it('checks', async () => {
const { p3 } = ctx;
const p3Toolbar = p3.getToolbar();
await p3.waitForSendMedia();
await ctx.p1.waitForRemoteVideo(await p3.getEndpointId());
// Mute audio on participant3.
await p3Toolbar.clickAudioMuteButton();
await ensureFourParticipants({
configOverwrite: {
channelLastN: 1
},
skipInMeetingChecks: true
});
const { p1, p2, p4 } = ctx;
await p4.waitForSendReceiveData();
// Mute audio on p4 and unmute p3.
await p4.getToolbar().clickAudioMuteButton();
await p3Toolbar.clickAudioUnmuteButton();
const p4EndpointId = await p4.getEndpointId();
const p3EndpointId = await p3.getEndpointId();
// Check if p1 starts receiving video from p3 and p4 shows up as ninja.
await p1.waitForNinjaIcon(p4EndpointId);
await p1.waitForRemoteVideo(p3EndpointId);
// At this point, mute video of p3 and others should be receiving p4's video.
// Mute p1's video
await p3Toolbar.clickVideoMuteButton();
await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p3);
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p3);
await p1.waitForRemoteVideo(p4EndpointId);
// Unmute p3's video and others should switch to receiving p3's video.
await p3Toolbar.clickVideoUnmuteButton();
await p1.waitForRemoteVideo(p3EndpointId);
await p1.waitForNinjaIcon(p4EndpointId);
// Mute p3's audio and unmute p2's audio. Other endpoints should continue to receive video from p3
// even though p2 is the dominant speaker.
await p3Toolbar.clickAudioMuteButton();
await p2.getToolbar().clickAudioUnmuteButton();
await p1.waitForRemoteVideo(p3EndpointId);
});
});

View File

@@ -1,145 +0,0 @@
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import {
checkForScreensharingTile,
ensureOneParticipant,
ensureTwoParticipants,
joinSecondParticipant,
} from '../../helpers/participants';
import {
muteAudioAndCheck,
unmuteAudioAndCheck,
unmuteVideoAndCheck
} from '../helpers/mute';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Mute', () => {
it('joining the meeting', () => ensureTwoParticipants());
it('mute p1 and check', () => toggleMuteAndCheck(ctx.p1, ctx.p2, true));
it('unmute p1 and check', () => toggleMuteAndCheck(ctx.p1, ctx.p2, false));
it('mute p2 and check', () => toggleMuteAndCheck(ctx.p2, ctx.p1, true));
it('unmute p2 and check', () => toggleMuteAndCheck(ctx.p2, ctx.p1, false));
it('p1 mutes p2 and check', async () => {
const { p1, p2 } = ctx;
if (!await p1.isModerator()) {
return;
}
await p1.getFilmstrip().muteAudio(p2);
// and now check whether second participant is muted
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p2);
});
it('p2 unmute after p1 mute and check', async () => {
const { p1, p2 } = ctx;
await unmuteAudioAndCheck(p2, p1);
});
it('p1 mutes before p2 joins', async () => {
await ctx.p2.hangup();
const { p1 } = ctx;
await p1.getToolbar().clickAudioMuteButton();
await ensureTwoParticipants();
const { p2 } = ctx;
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p1);
await toggleMuteAndCheck(p1, p2, false);
});
it('mute before join and screen share after in p2p', () => muteP1BeforeP2JoinsAndScreenshare(true));
it('mute before join and screen share after with jvb', () => muteP1BeforeP2JoinsAndScreenshare(false));
});
/**
* Toggles the mute state of a specific Meet conference participant and
* verifies that a specific other Meet conference participants sees a
* specific mute state for the former.
* @param testee The participant whose mute state is to be toggled.
* @param observer The participant to verify the mute state of {@code testee}.
* @param muted the mute state of {@code testee} expected to be observed by {@code observer}.
*/
async function toggleMuteAndCheck(
testee: Participant,
observer: Participant,
muted: boolean) {
if (muted) {
await muteAudioAndCheck(testee, observer);
} else {
await unmuteAudioAndCheck(testee, observer);
}
}
/**
* Video mutes participant1 before participant2 joins and checks if participant1 can share or unmute video
* and that media is being received on participant2 in both the cases.
*
* @param p2p whether to enable p2p or not.
*/
async function muteP1BeforeP2JoinsAndScreenshare(p2p: boolean) {
await Promise.all([ ctx.p1?.hangup(), ctx.p2?.hangup() ]);
await ensureOneParticipant({
configOverwrite: {
p2p: {
enabled: p2p
}
}
});
const { p1 } = ctx;
await p1.getToolbar().clickVideoMuteButton();
await joinSecondParticipant({
configOverwrite: {
p2p: {
enabled: p2p
}
}
});
const { p2 } = ctx;
if (p2p) {
await p2.waitForP2PIceConnected();
} else {
await p2.waitForIceConnected();
}
await p2.waitForSendMedia();
// Check if p1 appears video muted on p2.
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
// Start desktop share.
await p1.getToolbar().clickDesktopSharingButton();
await checkForScreensharingTile(p1, p2);
// we need to pass the id of the fake participant we use for the screensharing
await p2.waitForRemoteVideo(`${await p1.getEndpointId()}-v1`);
// Stop desktop share and unmute video and check for video again.
await p1.getToolbar().clickStopDesktopSharingButton();
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
await unmuteVideoAndCheck(p1, p2);
await p2.waitForRemoteVideo(await p1.getEndpointId());
}

View File

@@ -1,44 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Pinning', () => {
it('joining the meeting', () => ensureTwoParticipants());
it('p1 click on local', () => ctx.p1.getFilmstrip().pinParticipant(ctx.p1));
it('p1 click on remote', async () => {
await closeToolbarMenu();
const { p1, p2 } = ctx;
await p1.getFilmstrip().pinParticipant(p2);
});
it('p1 unpin remote', () => ctx.p1.getFilmstrip().unpinParticipant(ctx.p2));
it('p2 pin remote', () => ctx.p2.getFilmstrip().pinParticipant(ctx.p1));
it('p2 unpin remote', () => ctx.p2.getFilmstrip().unpinParticipant(ctx.p1));
it('p2 click on local', () => ctx.p2.getFilmstrip().pinParticipant(ctx.p2));
it('p2 click on remote', async () => {
await closeToolbarMenu();
const { p1, p2 } = ctx;
await p2.getFilmstrip().pinParticipant(p1);
});
});
/**
* Closes the overflow menu on both participants.
*/
async function closeToolbarMenu() {
await ctx.p1.getToolbar().closeOverflowMenu();
await ctx.p2.getToolbar().closeOverflowMenu();
}

View File

@@ -1,285 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import {
checkForScreensharingTile,
ensureOneParticipant,
ensureTwoParticipants,
hangupAllParticipants,
joinSecondParticipant,
joinThirdParticipant
} from '../../helpers/participants';
import { unmuteVideoAndCheck } from '../helpers/mute';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
describe('Start muted', () => {
it('checkboxes test', async () => {
const options = {
configOverwrite: {
p2p: {
enabled: true
},
testing: {
testMode: true,
debugAudioLevels: true
}
} };
await ensureOneParticipant(options);
const { p1 } = ctx;
const p1EndpointId = await p1.getEndpointId();
await p1.getToolbar().clickSettingsButton();
const settingsDialog = p1.getSettingsDialog();
await settingsDialog.waitForDisplay();
await settingsDialog.setStartAudioMuted(true);
await settingsDialog.setStartVideoMuted(true);
await settingsDialog.submit();
// Check that p1 doesn't get muted.
await p1.getFilmstrip().assertAudioMuteIconIsDisplayed(p1, true);
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1, true);
await joinSecondParticipant(options);
// Enable screenshare on p1.
p1.getToolbar().clickDesktopSharingButton();
await checkForScreensharingTile(p1, p1);
const { p2 } = ctx;
const p2EndpointId = await p2.getEndpointId();
await p2.waitForIceConnected();
await p2.waitForReceiveMedia();
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p2);
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
await p1.waitForAudioMuted(p2, true);
await p1.waitForRemoteVideo(p2EndpointId, true);
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p1, true);
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1, true);
await p2.waitForAudioMuted(p1, false);
await p2.waitForRemoteVideo(p1EndpointId, false);
// Check if a remote screenshare tile is created on p2.
await checkForScreensharingTile(p1, p2);
// Enable video on p2 and check if p2 appears unmuted on p1.
await Promise.all([
p2.getToolbar().clickAudioUnmuteButton(), p2.getToolbar().clickVideoUnmuteButton()
]);
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p2, true);
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
await p1.waitForAudioMuted(p2, false);
await p1.waitForRemoteVideo(p2EndpointId, false);
// Add a third participant and check p3 is able to receive audio and video from p2.
await joinThirdParticipant(options);
const { p3 } = ctx;
await p3.waitForIceConnected();
await p3.waitForReceiveMedia();
await p3.getFilmstrip().assertAudioMuteIconIsDisplayed(p2, true);
await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
await checkForScreensharingTile(p1, p3);
});
it('config options test', async () => {
await hangupAllParticipants();
const options = {
configOverwrite: {
testing: {
testMode: true,
debugAudioLevels: true
},
startAudioMuted: 2,
startVideoMuted: 2
}
};
await ensureOneParticipant(options);
await joinSecondParticipant(options);
const { p2 } = ctx;
await p2.waitForIceConnected();
await p2.waitForReceiveMedia();
await joinThirdParticipant(options);
const { p3 } = ctx;
await p3.waitForIceConnected();
await p3.waitForReceiveMedia();
const { p1 } = ctx;
const p2ID = await p2.getEndpointId();
await p1.log(`Start configOptionsTest, second participant: ${p2ID}`);
// Participant 3 should be muted, 1 and 2 unmuted.
await p3.getFilmstrip().assertAudioMuteIconIsDisplayed(p3);
await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p3);
await Promise.all([
p1.waitForAudioMuted(p3, true),
p2.waitForAudioMuted(p3, true)
]);
await p3.getFilmstrip().assertAudioMuteIconIsDisplayed(p1, true);
await p3.getFilmstrip().assertAudioMuteIconIsDisplayed(p2, true);
await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1, true);
await p3.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
// Unmute and see if the audio works
// we need 1 and 2 to be muted so we have a dominant speaker event for correct audio levels calculations
await p1.getToolbar().clickAudioMuteButton();
await p2.getToolbar().clickAudioMuteButton();
await p3.getToolbar().clickAudioUnmuteButton();
await p1.log('configOptionsTest, unmuted third participant');
await p1.waitForAudioMuted(p3, false /* unmuted */);
});
it('startWithVideoMuted=true can unmute', async () => {
// Maybe disable if there is FF or Safari participant.
await hangupAllParticipants();
// Explicitly enable P2P due to a regression with unmute not updating
// large video while in P2P.
const options = {
configOverwrite: {
p2p: {
enabled: true
},
startWithVideoMuted: true
}
};
await ensureTwoParticipants(options);
const { p1, p2 } = ctx;
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
await Promise.all([
p1.getLargeVideo().waitForSwitchTo(await p2.getEndpointId()),
p2.getLargeVideo().waitForSwitchTo(await p1.getEndpointId())
]);
await unmuteVideoAndCheck(p2, p1);
await p1.getLargeVideo().assertPlaying();
});
it('startWithAudioMuted=true can unmute', async () => {
await hangupAllParticipants();
const options = {
configOverwrite: {
startWithAudioMuted: true,
testing: {
testMode: true,
debugAudioLevels: true
}
}
};
await ensureTwoParticipants(options);
const { p1, p2 } = ctx;
await Promise.all([ p1.waitForAudioMuted(p2, true), p2.waitForAudioMuted(p1, true) ]);
await p1.getToolbar().clickAudioUnmuteButton();
await Promise.all([ p1.waitForAudioMuted(p2, true), p2.waitForAudioMuted(p1, false) ]);
});
it('startWithAudioVideoMuted=true can unmute', async () => {
await hangupAllParticipants();
const options = {
configOverwrite: {
startWithAudioMuted: true,
startWithVideoMuted: true,
p2p: {
enabled: true
}
}
};
await ensureOneParticipant(options);
await joinSecondParticipant({
configOverwrite: {
testing: {
testMode: true,
debugAudioLevels: true
},
p2p: {
enabled: true
}
}
});
const { p1, p2 } = ctx;
await p2.waitForIceConnected();
await p2.waitForSendMedia();
await p2.waitForAudioMuted(p1, true);
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
// Unmute p1's both audio and video and check on p2.
await p1.getToolbar().clickAudioUnmuteButton();
await p2.waitForAudioMuted(p1, false);
await unmuteVideoAndCheck(p1, p2);
await p2.getLargeVideo().assertPlaying();
});
it('test p2p JVB switch and switch back', async () => {
const { p1, p2 } = ctx;
// Mute p2's video just before p3 joins.
await p2.getToolbar().clickVideoMuteButton();
await joinThirdParticipant({
configOverwrite: {
p2p: {
enabled: true
}
}
});
const { p3 } = ctx;
// Unmute p2 and check if its video is being received by p1 and p3.
await unmuteVideoAndCheck(p2, p3);
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
// Mute p2's video just before p3 leaves.
await p2.getToolbar().clickVideoMuteButton();
await p3.hangup();
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
await p2.getToolbar().clickVideoUnmuteButton();
// Check if p2's video is playing on p1.
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2, true);
await p1.getLargeVideo().assertPlaying();
});
});

View File

@@ -1,47 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureTwoParticipants } from '../../helpers/participants';
import { muteVideoAndCheck, unmuteVideoAndCheck } from '../helpers/mute';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Stop video', () => {
it('joining the meeting', () => ensureTwoParticipants());
it('stop video and check', () => muteVideoAndCheck(ctx.p1, ctx.p2));
it('start video and check', () => unmuteVideoAndCheck(ctx.p1, ctx.p2));
it('start video and check stream', async () => {
await muteVideoAndCheck(ctx.p1, ctx.p2);
// now participant2 should be on large video
const largeVideoId = await ctx.p1.getLargeVideo().getId();
await unmuteVideoAndCheck(ctx.p1, ctx.p2);
// check if video stream from second participant is still on large video
expect(largeVideoId).toBe(await ctx.p1.getLargeVideo().getId());
});
it('stop video on participant and check', () => muteVideoAndCheck(ctx.p2, ctx.p1));
it('start video on participant and check', () => unmuteVideoAndCheck(ctx.p2, ctx.p1));
it('stop video on before second joins', async () => {
await ctx.p2.hangup();
const { p1 } = ctx;
await p1.getToolbar().clickVideoMuteButton();
await ensureTwoParticipants();
const { p2 } = ctx;
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
await unmuteVideoAndCheck(p1, p2);
});
});

View File

@@ -1,214 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureThreeParticipants, ensureTwoParticipants } from '../../helpers/participants';
import { unmuteVideoAndCheck } from '../helpers/mute';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
const EMAIL = 'support@jitsi.org';
const HASH = '38f014e4b7dde0f64f8157d26a8c812e';
describe('Avatar', () => {
it('setup the meeting', () =>
ensureTwoParticipants({
skipDisplayName: true
})
);
it('change and check', async () => {
const { p1, p2 } = ctx;
// check default avatar for p1 on p2
await p2.assertDefaultAvatarExist(p1);
await p1.getToolbar().clickProfileButton();
const settings = p1.getSettingsDialog();
await settings.waitForDisplay();
await settings.setEmail(EMAIL);
await settings.submit();
// check if the local avatar in the toolbar menu has changed
await p1.driver.waitUntil(
async () => (await p1.getToolbar().getProfileImage())?.includes(HASH), {
timeout: 3000, // give more time for the initial download of the image
timeoutMsg: 'Avatar has not changed for p1'
});
// check if the avatar in the local thumbnail has changed
expect(await p1.getLocalVideoAvatar()).toContain(HASH);
const p1EndpointId = await p1.getEndpointId();
await p2.driver.waitUntil(
async () => (await p2.getFilmstrip().getAvatar(p1EndpointId))?.includes(HASH), {
timeout: 5000,
timeoutMsg: 'Avatar has not changed for p1 on p2'
});
// check if the avatar in the large video has changed
expect(await p2.getLargeVideo().getAvatar()).toContain(HASH);
// we check whether the default avatar of participant2 is displayed on both sides
await p1.assertDefaultAvatarExist(p2);
await p2.assertDefaultAvatarExist(p2);
// the problem on FF where we can send keys to the input field,
// and the m from the text can mute the call, check whether we are muted
await p2.getFilmstrip().assertAudioMuteIconIsDisplayed(p1, true);
});
it('when video muted', async () => {
const { p1 } = ctx;
await ctx.p2.hangup();
// Mute p1's video
await p1.getToolbar().clickVideoMuteButton();
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
await p1.driver.waitUntil(
async () => (await p1.getLargeVideo().getAvatar())?.includes(HASH), {
timeout: 2000,
timeoutMsg: 'Avatar on large video did not change'
});
const p1LargeSrc = await p1.getLargeVideo().getAvatar();
const p1ThumbSrc = await p1.getLocalVideoAvatar();
// Check if avatar on large video is the same as on local thumbnail
expect(p1ThumbSrc).toBe(p1LargeSrc);
// Join p2
await ensureTwoParticipants({
skipDisplayName: true
});
const { p2 } = ctx;
// Verify that p1 is muted from the perspective of p2
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
await p2.getFilmstrip().pinParticipant(p1);
// Check if p1's avatar is on large video now
await p2.driver.waitUntil(
async () => await p2.getLargeVideo().getAvatar() === p1LargeSrc, {
timeout: 2000,
timeoutMsg: 'Avatar on large video did not change'
});
// p1 pins p2's video
await p1.getFilmstrip().pinParticipant(p2);
// Check if avatar is displayed on p1's local video thumbnail
await p1.assertThumbnailShowsAvatar(p1, false, false, true);
// Unmute - now local avatar should be hidden and local video displayed
await unmuteVideoAndCheck(p1, p2);
await p1.asserLocalThumbnailShowsVideo();
// Now both p1 and p2 have video muted
await p1.getToolbar().clickVideoMuteButton();
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1);
await p2.getToolbar().clickVideoMuteButton();
await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
await p1.getParticipantsPane().assertVideoMuteIconIsDisplayed(p2);
// Start the third participant
await ensureThreeParticipants({
skipInMeetingChecks: true
});
const { p3 } = ctx;
// When the first participant is FF because of their audio mic feed it will never become dominant speaker
// and no audio track will be received by the third participant and video is muted,
// that's why we need to do a different check that expects any track just from p2
if (p1.driver.isFirefox) {
await Promise.all([ p2.waitForRemoteStreams(1), p3.waitForRemoteStreams(1) ]);
} else {
await Promise.all([ p2.waitForRemoteStreams(2), p3.waitForRemoteStreams(2) ]);
}
// Pin local video and verify avatars are displayed
await p3.getFilmstrip().pinParticipant(p3);
await p3.assertThumbnailShowsAvatar(p1, false, false, true);
await p3.assertThumbnailShowsAvatar(p2, false, true);
const p1EndpointId = await p1.getEndpointId();
const p2EndpointId = await p2.getEndpointId();
expect(await p3.getFilmstrip().getAvatar(p1EndpointId)).toBe(p1ThumbSrc);
// Click on p1's video
await p3.getFilmstrip().pinParticipant(p1);
// The avatar should be on large video and display name instead of an avatar, local video displayed
await p3.driver.waitUntil(
async () => await p3.getLargeVideo().getResource() === p1EndpointId, {
timeout: 2000,
timeoutMsg: `Large video did not switch to ${p1.name}`
});
await p3.assertDisplayNameVisibleOnStage(
await p3.getFilmstrip().getRemoteDisplayName(p1EndpointId));
// p2 has the default avatar
await p3.assertThumbnailShowsAvatar(p2, false, true);
await p3.assertThumbnailShowsAvatar(p3, true);
// Click on p2's video
await p3.getFilmstrip().pinParticipant(p2);
// The avatar should be on large video and display name instead of an avatar, local video displayed
await p3.driver.waitUntil(
async () => await p3.getLargeVideo().getResource() === p2EndpointId, {
timeout: 2000,
timeoutMsg: `Large video did not switch to ${p2.name}`
});
await p3.assertDisplayNameVisibleOnStage(
await p3.getFilmstrip().getRemoteDisplayName(p2EndpointId)
);
await p3.assertThumbnailShowsAvatar(p1, false, false, true);
await p3.assertThumbnailShowsAvatar(p3, true);
await p3.hangup();
// Unmute p1's and p2's videos
await unmuteVideoAndCheck(p1, p2);
});
it('email persistence', async () => {
let { p1 } = ctx;
if (p1.driver.isFirefox) {
// strangely this test when FF is involved, missing source mapping from jvb
// and fails with an error of: expected number of remote streams:1 in 15s for participant1
return;
}
await p1.getToolbar().clickProfileButton();
expect(await p1.getSettingsDialog().getEmail()).toBe(EMAIL);
await p1.hangup();
await ensureTwoParticipants({
skipDisplayName: true
});
p1 = ctx.p1;
await p1.getToolbar().clickProfileButton();
expect(await p1.getSettingsDialog().getEmail()).toBe(EMAIL);
});
});

View File

@@ -1,486 +0,0 @@
import type { ChainablePromiseElement } from 'webdriverio';
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import {
checkSubject,
ensureThreeParticipants,
ensureTwoParticipants,
hangupAllParticipants
} from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
const MAIN_ROOM_NAME = 'Main room';
const BREAKOUT_ROOMS_LIST_ID = 'breakout-rooms-list';
const LIST_ITEM_CONTAINER = 'list-item-container';
describe('Breakout rooms', () => {
it('check support', async () => {
await ensureTwoParticipants();
if (!await ctx.p1.isBreakoutRoomsSupported()) {
ctx.skipSuiteTests = 'The environment does not support breakout rooms.';
}
});
it('add breakout room', async () => {
const { p1, p2 } = ctx;
const p1BreakoutRooms = p1.getBreakoutRooms();
// there should be no breakout rooms initially, list is sent with a small delay
await p1.driver.pause(2000);
expect(await p1BreakoutRooms.getRoomsCount()).toBe(0);
// add one breakout room
await p1BreakoutRooms.addBreakoutRoom();
await p1.driver.waitUntil(
async () => await p1BreakoutRooms.getRoomsCount() === 1, {
timeout: 5000,
timeoutMsg: 'No breakout room added for p1'
});
// second participant should also see one breakout room
await p2.driver.waitUntil(
async () => await p2.getBreakoutRooms().getRoomsCount() === 1, {
timeout: 5000,
timeoutMsg: 'No breakout room seen by p2'
});
});
it('join breakout room', async () => {
const { p1, p2 } = ctx;
const p1BreakoutRooms = p1.getBreakoutRooms();
// there should be one breakout room
await p1.driver.waitUntil(
async () => await p1BreakoutRooms.getRoomsCount() === 1, {
timeout: 5000,
timeoutMsg: 'No breakout room seen by p1'
});
const roomsList = await p1BreakoutRooms.getRooms();
expect(roomsList.length).toBe(1);
// join the room
await roomsList[0].joinRoom();
// the participant should see the main room as the only breakout room
await p1.driver.waitUntil(
async () => {
if (await p1BreakoutRooms.getRoomsCount() !== 1) {
return false;
}
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 1) {
return false;
}
return list[0].name === MAIN_ROOM_NAME;
}, {
timeout: 5000,
timeoutMsg: 'P1 did not join breakout room'
});
// the second participant should see one participant in the breakout room
await p2.driver.waitUntil(
async () => {
const list = await p2.getBreakoutRooms().getRooms();
if (list?.length !== 1) {
return false;
}
return list[0].participantCount === 1;
}, {
timeout: 5000,
timeoutMsg: 'P2 is not seeing p1 in the breakout room'
});
});
it('leave breakout room', async () => {
const { p1, p2 } = ctx;
const p1BreakoutRooms = p1.getBreakoutRooms();
// leave room
await p1BreakoutRooms.leaveBreakoutRoom();
// there should be one breakout room and that should not be the main room
await p1.driver.waitUntil(
async () => {
if (await p1BreakoutRooms.getRoomsCount() !== 1) {
return false;
}
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 1) {
return false;
}
return list[0].name !== MAIN_ROOM_NAME;
}, {
timeout: 5000,
timeoutMsg: 'P1 did not leave breakout room'
});
// the second participant should see no participants in the breakout room
await p2.driver.waitUntil(
async () => {
const list = await p2.getBreakoutRooms().getRooms();
if (list?.length !== 1) {
return false;
}
return list[0].participantCount === 0;
}, {
timeout: 5000,
timeoutMsg: 'P2 is seeing p1 in the breakout room'
});
});
it('remove breakout room', async () => {
const { p1, p2 } = ctx;
const p1BreakoutRooms = p1.getBreakoutRooms();
// remove the room
await (await p1BreakoutRooms.getRooms())[0].removeRoom();
// there should be no breakout rooms
await p1.driver.waitUntil(
async () => await p1BreakoutRooms.getRoomsCount() === 0, {
timeout: 5000,
timeoutMsg: 'Breakout room was not removed for p1'
});
// the second participant should also see no breakout rooms
await p2.driver.waitUntil(
async () => await p2.getBreakoutRooms().getRoomsCount() === 0, {
timeout: 5000,
timeoutMsg: 'Breakout room was not removed for p2'
});
});
it('auto assign', async () => {
await ensureThreeParticipants();
const { p1, p2 } = ctx;
const p1BreakoutRooms = p1.getBreakoutRooms();
// create two rooms
await p1BreakoutRooms.addBreakoutRoom();
await p1BreakoutRooms.addBreakoutRoom();
// there should be two breakout rooms
await p1.driver.waitUntil(
async () => await p1BreakoutRooms.getRoomsCount() === 2, {
timeout: 5000,
timeoutMsg: 'Breakout room was not created by p1'
});
// auto assign participants to rooms
await p1BreakoutRooms.autoAssignToBreakoutRooms();
// each room should have one participant
await p1.driver.waitUntil(
async () => {
if (await p1BreakoutRooms.getRoomsCount() !== 2) {
return false;
}
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 2) {
return false;
}
return list[0].participantCount === 1 && list[1].participantCount === 1;
}, {
timeout: 5000,
timeoutMsg: 'P1 did not auto assigned participants to breakout rooms'
});
// the second participant should see one participant in the main room
const p2BreakoutRooms = p2.getBreakoutRooms();
await p2.driver.waitUntil(
async () => {
if (await p2BreakoutRooms.getRoomsCount() !== 2) {
return false;
}
const list = await p2BreakoutRooms.getRooms();
if (list?.length !== 2) {
return false;
}
return list[0].participantCount === 1 && list[1].participantCount === 1
&& (list[0].name === MAIN_ROOM_NAME || list[1].name === MAIN_ROOM_NAME);
}, {
timeout: 5000,
timeoutMsg: 'P2 is not seeing p1 in the main room'
});
});
it('close breakout room', async () => {
const { p1, p2, p3 } = ctx;
const p1BreakoutRooms = p1.getBreakoutRooms();
// there should be two non-empty breakout rooms
await p1.driver.waitUntil(
async () => {
if (await p1BreakoutRooms.getRoomsCount() !== 2) {
return false;
}
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 2) {
return false;
}
return list[0].participantCount === 1 && list[1].participantCount === 1;
}, {
timeout: 5000,
timeoutMsg: 'P1 is not seeing two breakout rooms'
});
// close the first room
await (await p1BreakoutRooms.getRooms())[0].closeRoom();
// there should be two rooms and first one should be empty
await p1.driver.waitUntil(
async () => {
if (await p1BreakoutRooms.getRoomsCount() !== 2) {
return false;
}
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 2) {
return false;
}
return list[0].participantCount === 0 || list[1].participantCount === 0;
}, {
timeout: 5000,
timeoutMsg: 'P1 is not seeing an empty breakout room'
});
// there should be two participants in the main room, either p2 or p3 got moved to the main room
const checkParticipants = (p: Participant) =>
p.driver.waitUntil(
async () => {
const isInBreakoutRoom = await p.isInBreakoutRoom();
const breakoutRooms = p.getBreakoutRooms();
if (isInBreakoutRoom) {
if (await breakoutRooms.getRoomsCount() !== 2) {
return false;
}
const list = await breakoutRooms.getRooms();
if (list?.length !== 2) {
return false;
}
return list.every(r => { // eslint-disable-line arrow-body-style
return r.name === MAIN_ROOM_NAME ? r.participantCount === 2 : r.participantCount === 0;
});
}
if (await breakoutRooms.getRoomsCount() !== 2) {
return false;
}
const list = await breakoutRooms.getRooms();
if (list?.length !== 2) {
return false;
}
return list[0].participantCount + list[1].participantCount === 1;
}, {
timeout: 5000,
timeoutMsg: `${p.name} is not seeing an empty breakout room and one with one participant`
});
await checkParticipants(p2);
await checkParticipants(p3);
});
it('send participants to breakout room', async () => {
await hangupAllParticipants();
// because the participants rejoin so fast, the meeting is not properly ended,
// so the previous breakout rooms would still be there.
// To avoid this issue we use a different meeting
// Respect room name suffix as it is important in multi-shard testing
ctx.roomName += `new-${ctx.roomName}`;
await ensureTwoParticipants();
const { p1, p2 } = ctx;
const p1BreakoutRooms = p1.getBreakoutRooms();
// there should be no breakout rooms
expect(await p1BreakoutRooms.getRoomsCount()).toBe(0);
// add one breakout room
await p1BreakoutRooms.addBreakoutRoom();
// there should be one empty room
await p1.driver.waitUntil(
async () => await p1BreakoutRooms.getRoomsCount() === 1
&& (await p1BreakoutRooms.getRooms())[0].participantCount === 0, {
timeout: 5000,
timeoutMsg: 'No breakout room added for p1'
});
// send the second participant to the first breakout room
await p1BreakoutRooms.sendParticipantToBreakoutRoom(p2, (await p1BreakoutRooms.getRooms())[0].name);
// there should be one room with one participant
await p1.driver.waitUntil(
async () => {
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 1) {
return false;
}
return list[0].participantCount === 1;
}, {
timeout: 5000,
timeoutMsg: 'P1 is not seeing p2 in the breakout room'
});
});
it('collapse breakout room', async () => {
const { p1 } = ctx;
const p1BreakoutRooms = p1.getBreakoutRooms();
// there should be one breakout room with one participant
await p1.driver.waitUntil(
async () => {
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 1) {
return false;
}
return list[0].participantCount === 1;
}, {
timeout: 5000,
timeoutMsg: 'P1 is not seeing p2 in the breakout room'
});
// get id of the breakout room participant
const breakoutList = p1.driver.$(`#${BREAKOUT_ROOMS_LIST_ID}`);
const breakoutRoomItem = await breakoutList.$$(`.${LIST_ITEM_CONTAINER}`).find(
async el => {
const id = await el.getAttribute('id');
return id !== '' && id !== null;
}) as ChainablePromiseElement;
const pId = await breakoutRoomItem.getAttribute('id');
const breakoutParticipant = p1.driver.$(`//div[@id="${pId}"]`);
expect(await breakoutParticipant.isDisplayed()).toBe(true);
// collapse the first
await (await p1BreakoutRooms.getRooms())[0].collapse();
// the participant should not be visible
expect(await breakoutParticipant.isDisplayed()).toBe(false);
// the collapsed room should still have one participant
expect((await p1BreakoutRooms.getRooms())[0].participantCount).toBe(1);
});
it('rename breakout room', async () => {
const myNewRoomName = `breakout-${crypto.randomUUID()}`;
const { p1, p2 } = ctx;
const p1BreakoutRooms = p1.getBreakoutRooms();
// let's rename breakout room and see it in local and remote
await (await p1BreakoutRooms.getRooms())[0].renameRoom(myNewRoomName);
await p1.driver.waitUntil(
async () => {
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 1) {
return false;
}
return list[0].name === myNewRoomName;
}, {
timeout: 5000,
timeoutMsg: 'The breakout room was not renamed for p1'
});
await checkSubject(p2, myNewRoomName);
const p2BreakoutRooms = p2.getBreakoutRooms();
// leave room
await p2BreakoutRooms.leaveBreakoutRoom();
// there should be one empty room
await p1.driver.waitUntil(
async () => {
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 1) {
return false;
}
return list[0].participantCount === 0;
}, {
timeout: 5000,
timeoutMsg: 'The breakout room not found or not empty for p1'
});
await p2.driver.waitUntil(
async () => {
const list = await p2BreakoutRooms.getRooms();
return list?.length === 1;
}, {
timeout: 5000,
timeoutMsg: 'The breakout room not seen by p2'
});
expect((await p2BreakoutRooms.getRooms())[0].name).toBe(myNewRoomName);
// send the second participant to the first breakout room
await p1BreakoutRooms.sendParticipantToBreakoutRoom(p2, myNewRoomName);
// there should be one room with one participant
await p1.driver.waitUntil(
async () => {
const list = await p1BreakoutRooms.getRooms();
if (list?.length !== 1) {
return false;
}
return list[0].participantCount === 1;
}, {
timeout: 5000,
timeoutMsg: 'The breakout room was not rename for p1'
});
await checkSubject(p2, myNewRoomName);
});
});

View File

@@ -1,54 +0,0 @@
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { joinMuc, waitForMedia } from '../../helpers/joinMuc';
setTestProperties(__filename, {
description: 'This test asserts that the connection to JVB is over UDP and using the same remote port. ',
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Connectivity', () => {
let p1: Participant, p2: Participant;
it('setup', async () => {
p1 = await joinMuc({ name: 'p1', token: testsConfig.jwt.preconfiguredToken });
p2 = await joinMuc({ name: 'p2', token: testsConfig.jwt.preconfiguredToken });
await waitForMedia([ p1, p2 ]);
});
it('protocol', async () => {
expect(await getProtocol(p1)).toBe('udp');
expect(await getProtocol(p2)).toBe('udp');
});
it('port', async () => {
const port1 = await getRemotePort(p1);
const port2 = await getRemotePort(p2);
expect(Number.isInteger(port1)).toBe(true);
expect(Number.isInteger(port2)).toBe(true);
expect(port1).toBe(port2);
});
});
/**
* Get the remote port of the participant.
* @param participant
*/
async function getRemotePort(participant: Participant) {
const data = await participant.execute(() => APP?.conference?.getStats()?.transport[0]?.ip);
const parts = data.split(':');
return parts.length > 1 ? parseInt(parts[1], 10) : '';
}
/**
* Get the remote port of the participant.
* @param participant
*/
async function getProtocol(participant: Participant) {
const data = await participant.execute(() => APP?.conference?.getStats()?.transport[0]?.type);
return data.toLowerCase();
}

View File

@@ -1,120 +0,0 @@
import process from 'node:process';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { expectations } from '../../helpers/expectations';
import { ensureOneParticipant } from '../../helpers/participants';
import {
assertDialInDisplayed,
assertUrlDisplayed,
cleanup,
dialIn,
isDialInEnabled, verifyMoreNumbersPage,
waitForAudioFromDialInParticipant
} from '../helpers/DialIn';
setTestProperties(__filename, {
usesBrowsers: [ 'p1' ]
});
/**
* This test is configured with two options:
* 1. The dialIn.enabled expectation. If set to true we assert the config.js settings for dial-in are set, the dial-in
* panel (including the PIN number) is displayed in the UI, and the "more numbers" page is displayed. If it's set to
* false we assert the config.js settings are not set, and the PIN is not displayed.
* 2. The DIAL_IN_REST_URL environment variable. If this is set and the environment supports dial-in, we invite a
* dial-in participant via this URL and assert that it joins the conference and sends media.
*/
describe('Dial-in', () => {
it('join participant', async () => {
// The same cases are covered for JaaS in jaas/dial/dialin.spec.ts.
if (testsConfig.jaas.enabled) {
ctx.skipSuiteTests = 'JaaS is configured.';
return;
}
await ensureOneParticipant();
expect(await ctx.p1.isInMuc()).toBe(true);
});
it('dial in config.js values', async function() {
if (expectations.dialIn.enabled === true) {
expect(await isDialInEnabled(ctx.p1)).toBe(expectations.dialIn.enabled);
} else {
// eslint-disable-next-line @typescript-eslint/no-invalid-this
this.skip();
}
});
it('open/close invite dialog', async () => {
await ctx.p1.getInviteDialog().open();
await ctx.p1.getInviteDialog().clickCloseButton();
await ctx.p1.getInviteDialog().waitTillOpen(true);
});
it('dial-in displayed', async function() {
if (expectations.dialIn.enabled !== null) {
await assertDialInDisplayed(ctx.p1, expectations.dialIn.enabled);
} else {
// eslint-disable-next-line @typescript-eslint/no-invalid-this
this.skip();
}
});
it('skip the rest if dial-in is not expected', async () => {
if (!expectations.dialIn.enabled) {
ctx.skipSuiteTests = 'Dial-in is not expected';
}
});
it('url displayed', () => assertUrlDisplayed(ctx.p1));
it('view more numbers page', async () => {
await verifyMoreNumbersPage(ctx.p1);
});
it('retrieve pin', async () => {
let dialInPin: string;
try {
dialInPin = await ctx.p1.getDialInPin();
} catch (e) {
console.error('dial-in.test.no-pin');
ctx.skipSuiteTests = 'No dial-in pin is available.';
throw e;
}
if (dialInPin.length === 0) {
console.error('dial-in.test.no-pin');
ctx.skipSuiteTests = 'The dial-in pin is empty.';
throw new Error('no pin');
}
expect(dialInPin.length >= 8).toBe(true);
});
it('skip the rest if a dial-in URL is not configured', async () => {
if (!process.env.DIAL_IN_REST_URL) {
ctx.skipSuiteTests = 'DIAL_IN_REST_URL is not set.';
}
});
it('invite dial-in participant', async () => {
await dialIn(await ctx.p1.getDialInPin());
});
it('wait for audio from dial-in participant', async () => {
const { p1 } = ctx;
if (!await p1.isInMuc()) {
// local participant did not join abort
return;
}
await waitForAudioFromDialInParticipant(p1);
await cleanup(p1);
});
});

View File

@@ -1,98 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureThreeParticipants, ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
describe('Follow me', () => {
it('joining the meeting', async () => {
await ensureTwoParticipants();
const { p1 } = ctx;
await p1.getToolbar().clickSettingsButton();
const settings = p1.getSettingsDialog();
await settings.waitForDisplay();
await settings.setFollowMe(true);
await settings.submit();
});
it('follow me checkbox visible only for moderators', async () => {
const { p2 } = ctx;
if (!await p2.isModerator()) {
await p2.getToolbar().clickSettingsButton();
const settings = p2.getSettingsDialog();
await settings.waitForDisplay();
expect(await settings.isFollowMeDisplayed()).toBe(false);
await settings.clickCloseButton();
}
});
it('filmstrip commands', async () => {
const { p1, p2 } = ctx;
const p1Filmstrip = p1.getFilmstrip();
const p2Filmstrip = p2.getFilmstrip();
await p1Filmstrip.toggle();
await p1Filmstrip.assertRemoteVideosHidden();
await p2Filmstrip.assertRemoteVideosHidden();
});
it('tile view', async () => {
await ensureThreeParticipants();
const { p1, p2, p3 } = ctx;
await p1.waitForTileViewDisplayed();
await p1.getToolbar().clickExitTileViewButton();
await Promise.all([
p1.waitForTileViewDisplayed(true),
p2.waitForTileViewDisplayed(true),
p3.waitForTileViewDisplayed(true)
]);
await p1.getToolbar().clickEnterTileViewButton();
await Promise.all([
p1.waitForTileViewDisplayed(),
p2.waitForTileViewDisplayed(),
p3.waitForTileViewDisplayed()
]);
});
it('next on stage', async () => {
const { p1, p2, p3 } = ctx;
await p1.getFilmstrip().pinParticipant(p2);
const p2Filmstrip = p2.getFilmstrip();
const localVideoId = await p2Filmstrip.getLocalVideoId();
await p2.driver.waitUntil(
async () => await localVideoId === await p2.getLargeVideo().getId(),
{
timeout: 5_000,
timeoutMsg: 'The pinned participant is not displayed on stage for p2'
});
const p2VideoIdOnp3 = await p3.getFilmstrip().getRemoteVideoId(await p2.getEndpointId());
await p3.driver.waitUntil(
async () => p2VideoIdOnp3 === await p3.getLargeVideo().getId(),
{
timeout: 5_000,
timeoutMsg: 'The pinned participant is not displayed on stage for p3'
});
});
});

View File

@@ -1,128 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Polls', () => {
it('joining the meeting', async () => {
await ensureTwoParticipants();
});
it('create poll', async () => {
const { p1 } = ctx;
await p1.getToolbar().clickChatButton();
expect(await p1.getChatPanel().isOpen()).toBe(true);
expect(await p1.getChatPanel().isPollsTabVisible()).toBe(false);
await p1.getChatPanel().openPollsTab();
expect(await p1.getChatPanel().isPollsTabVisible()).toBe(true);
// create poll
await p1.getChatPanel().clickCreatePollButton();
await p1.getChatPanel().waitForNewPollInput();
});
it('fill in poll', async () => {
const { p1 } = ctx;
await p1.getChatPanel().fillPollQuestion('My Poll question?');
await p1.getChatPanel().waitForOptionInput(0);
await p1.getChatPanel().waitForOptionInput(1);
await p1.getChatPanel().fillPollOption(0, 'First option');
await p1.getChatPanel().fillPollOption(1, 'Second option');
await p1.getChatPanel().clickAddOptionButton();
await p1.getChatPanel().waitForOptionInput(2);
await p1.getChatPanel().fillPollOption(2, 'Third option');
await p1.getChatPanel().clickAddOptionButton();
await p1.getChatPanel().waitForOptionInput(3);
await p1.getChatPanel().fillPollOption(3, 'Fourth option');
await p1.getChatPanel().clickRemoveOptionButton(2);
// we remove the option and reindexing happens, so we check for index 3
await p1.getChatPanel().waitForOptionInputNonExisting(3);
expect(await p1.getChatPanel().getOption(2)).toBe('Fourth option');
});
it('save and edit poll', async () => {
const { p1 } = ctx;
await p1.getChatPanel().clickSavePollButton();
await p1.getChatPanel().waitForSendButton();
await p1.getChatPanel().clickEditPollButton();
await p1.getChatPanel().fillPollOption(0, ' edited!');
await p1.getChatPanel().clickSavePollButton();
await p1.getChatPanel().waitForSendButton();
});
it('send poll', async () => {
const { p1 } = ctx;
await p1.getChatPanel().clickSendPollButton();
});
it('vote on poll', async () => {
const { p1 } = ctx;
// await p1.getNotifications().closePollsNotification();
// we have only one poll, so we get its ID
const pollId: string = await p1.driver.waitUntil(() => p1.driver.execute(() => {
return Object.keys(APP.store.getState()['features/polls'].polls)[0];
}), { timeout: 2000 });
// we have just send the poll, so the UI should be in a state for voting
await p1.getChatPanel().voteForOption(pollId, 0);
});
it('check for vote', async () => {
const { p1, p2 } = ctx;
const pollId: string = await p1.driver.execute('return Object.keys(APP.store.getState()["features/polls"].polls)[0];');
// now let's check on p2 side
await p2.getToolbar().clickChatButton();
expect(await p2.getChatPanel().isOpen()).toBe(true);
expect(await p2.getChatPanel().isPollsTabVisible()).toBe(false);
await p2.getChatPanel().openPollsTab();
expect(await p2.getChatPanel().isPollsTabVisible()).toBe(true);
expect(await p2.getChatPanel().isPollVisible(pollId));
await p2.getChatPanel().clickSkipPollButton();
expect(await p2.getChatPanel().getResult(pollId, 0)).toBe('1 (100%)');
});
it('leave and check for vote', async () => {
await ctx.p2.hangup();
await ensureTwoParticipants();
const { p1, p2 } = ctx;
const pollId: string = await p1.driver.execute('return Object.keys(APP.store.getState()["features/polls"].polls)[0];');
await p2.getToolbar().clickChatButton();
await p2.getChatPanel().openPollsTab();
expect(await p2.getChatPanel().isPollVisible(pollId));
await p2.getChatPanel().clickSkipPollButton();
expect(await p2.getChatPanel().getResult(pollId, 0)).toBe('1 (100%)');
});
});

View File

@@ -1,37 +0,0 @@
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
const MY_TEST_SUBJECT = 'My Test Subject';
const SUBJECT_XPATH = '//div[starts-with(@class, "subject-text")]';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Subject', () => {
it('setup', async () => {
await ensureOneParticipant({
configOverwrite: {
subject: MY_TEST_SUBJECT
}
});
await ensureTwoParticipants();
});
it('subject set locally', async () => await checkSubject(ctx.p1, MY_TEST_SUBJECT));
it('subject set remotely', async () => await checkSubject(ctx.p2, MY_TEST_SUBJECT));
});
/**
* Check was subject set.
*
* @param participant
* @param subject
*/
async function checkSubject(participant: Participant, subject: string) {
const localTile = participant.driver.$(SUBJECT_XPATH);
await localTile.moveTo();
expect(await localTile.getText()).toBe(subject);
}

View File

@@ -1,84 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { expectations } from '../../helpers/expectations';
import { joinMuc } from '../../helpers/joinMuc';
setTestProperties(__filename, {
usesBrowsers: [ 'p1' ]
});
describe('URL normalisation', () => {
// If we're not able to create conferences with a custom tenant, we'll only test the room name.
const useTenant = expectations.useTenant;
const tests = [
{
hint: '@ sign and .',
// Room as entered in the URL
room: '@example.com',
// Room as normalized in the URL
roomUrl: 'example.com',
// The room part of the MUC JID
roomJid: 'example.com',
// Tenant as entered in the URL
tenant: 'tenant@example.com',
// Tenant as normalized in the URL
tenantUrl: 'tenantexample.com',
// The tenant part of the MUC JID
tenantJid: 'tenantexample_com'
},
{
hint: 'Dashes',
room: 'foo-bar',
roomUrl: 'foo-bar',
roomJid: 'foo-bar',
tenant: 'tenant-example.com',
tenantUrl: 'tenant-example.com',
tenantJid: 'tenant-example_com'
},
{
hint: 'Cyrillic',
room: 'фоо-бар',
roomUrl: '%D1%84%D0%BE%D0%BE-%D0%B1%D0%B0%D1%80',
roomJid: '%d1%84%d0%be%d0%be-%d0%b1%d0%b0%d1%80',
tenant: 'обитател',
tenantUrl: '%D0%BE%D0%B1%D0%B8%D1%82%D0%B0%D1%82%D0%B5%D0%BB',
tenantJid: '%d0%be%d0%b1%d0%b8%d1%82%d0%b0%d1%82%d0%b5%d0%bb',
}
];
for (const test of tests) {
it(test.hint, async () => {
const fullRoom = `${test.room}${ctx.roomName}`;
const fullRoomUrl = `${test.roomUrl}${ctx.roomName}`;
const tenant = useTenant ? test.tenant : undefined;
const p = await joinMuc({
name: 'p1',
token: testsConfig.jwt.preconfiguredToken,
}, {
tenant: tenant,
roomName: fullRoom
});
const currentUrlStr = await p.driver.getUrl();
const currentUrl = new URL(currentUrlStr);
const path = currentUrl.pathname;
const parts = path.split('/');
if (useTenant) {
expect(parts[1]).toBe(test.tenantUrl);
}
expect(parts[2]).toBe(fullRoomUrl);
const mucJid = (await p.execute(() => APP.conference._room.room.roomjid)).split('@');
const roomJid = mucJid[0];
const domain = mucJid[1];
expect(roomJid).toBe(`${test.roomJid}${ctx.roomName}`);
if (useTenant) {
expect(domain.startsWith(`conference.${test.tenantJid}.`)).toBe(true);
}
});
}
});

View File

@@ -1,55 +0,0 @@
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { expectations } from '../../helpers/expectations';
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
description: 'This test checks the ability of a moderator to grant the "moderator" role to another participant. The\
test is skipped when the "allModerators" expectation is set.',
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Grant moderator', () => {
let p1: Participant, p2: Participant;
it('setup', async () => {
if (expectations.moderation.allModerators) {
ctx.skipSuiteTests = 'allModerators is expected';
return;
}
await ensureOneParticipant();
p1 = ctx.p1;
expect(await p1.isModerator()).toBe(true);
const functionAvailable = await p1.execute(() => typeof APP.conference._room.grantOwner === 'function');
if (expectations.moderation.grantModerator) {
expect(functionAvailable).toBe(true);
} else {
if (!functionAvailable) {
ctx.skipSuiteTests = 'grantModerator is not available and not expected';
return;
}
}
await ensureTwoParticipants();
p2 = ctx.p2;
expect(await p2.isModerator()).toBe(false);
});
it('grant moderator', async () => {
await p1.getFilmstrip().grantModerator(p2);
await p2.driver.waitUntil(
() => p2.isModerator(),
{
timeout: 3000,
timeoutMsg: 'p2 did not become moderator'
}
);
});
});

View File

@@ -1,75 +0,0 @@
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { expectations } from '../../helpers/expectations';
import { ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Kick', () => {
let p1: Participant, p2: Participant;
it('setup', async () => {
await ensureTwoParticipants();
p1 = ctx.p1;
p2 = ctx.p2;
// We verify elsewhere (moderation.spec.ts) that the first participant is a moderator.
if (!await p1.isModerator()) {
ctx.skipSuiteTests = 'the first participant is not a moderator';
}
});
it('kick (p2p disabled)', () => kickAndCheck(p1, p2));
it('setup (p2p enabled)', async () => {
await p1.hangup();
await p2.hangup();
await ensureTwoParticipants({
configOverwrite: {
p2p: {
enabled: true
}
}
});
p1 = ctx.p1;
p2 = ctx.p2;
});
it('kick (p2p enabled)', async () => {
await kickAndCheck(p1, p2);
});
it('non-moderator cannot kick', async () => {
if (!expectations.moderation.allModerators) {
await ensureTwoParticipants();
p2 = ctx.p2;
expect(await p2.isModerator()).toBe(false);
await p2.execute(
epId => APP.conference._room.kickParticipant(epId, 'for funzies'),
await p1.getEndpointId()
);
await p1.driver.pause(3000);
expect(await p1.isInMuc()).toBe(true);
}
});
});
/**
* Kicks the second participant and checks that the participant is removed from the conference and that dialogue is open.
*/
async function kickAndCheck(kicker: Participant, kickee: Participant) {
await kicker.getFilmstrip().kickParticipant(await kickee.getEndpointId());
await kicker.waitForParticipants(0);
// check that the kicked participant sees the kick reason dialog
await kickee.driver.waitUntil(
async () => kickee.isLeaveReasonDialogOpen(), {
timeout: 2000,
timeoutMsg: 'No leave reason dialog shown for p2'
});
}

View File

@@ -1,517 +0,0 @@
import { P1, P3, Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { expectations } from '../../helpers/expectations';
import {
ensureOneParticipant,
ensureThreeParticipants,
ensureTwoParticipants,
hangupAllParticipants
} from '../../helpers/participants';
import type { IJoinOptions } from '../../helpers/types';
import type PreMeetingScreen from '../../pageobjects/PreMeetingScreen';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
describe('Lobby', () => {
it('joining the meeting', async () => {
await ensureOneParticipant();
if (!await ctx.p1.execute(() => APP.conference._room.isLobbySupported())) {
ctx.skipSuiteTests = 'The environment does not support lobby.';
}
});
it('enable', async () => {
await ensureTwoParticipants();
await enableLobby();
});
it('entering in lobby and approve', async () => {
const { p1, p2 } = ctx;
await enterLobby(p1, true);
const { p3 } = ctx;
await p1.getNotifications().allowLobbyParticipant();
const notificationText = await p2.getNotifications().getLobbyParticipantAccessGranted();
expect(notificationText.includes(P1)).toBe(true);
expect(notificationText.includes(P3)).toBe(true);
await p2.getNotifications().closeLobbyParticipantAccessGranted();
// ensure 3 participants in the call will check for the third one that muc is joined, ice connected,
// media is being receiving and there are two remote streams
await p3.waitToJoinMUC();
await p3.waitForIceConnected();
await p3.waitForSendReceiveData();
await p3.waitForRemoteStreams(2);
// now check third one display name in the room, is the one set in the prejoin screen
const name = await p1.getFilmstrip().getRemoteDisplayName(await p3.getEndpointId());
expect(name).toBe(P3);
await p3.hangup();
});
it('entering in lobby and deny', async () => {
const { p1, p2 } = ctx;
// the first time tests is executed we need to enter display name,
// for next execution that will be locally stored
await enterLobby(p1, false);
// moderator rejects access
await p1.getNotifications().rejectLobbyParticipant();
// deny notification on 2nd participant
const notificationText = await p2.getNotifications().getLobbyParticipantAccessDenied();
expect(notificationText.includes(P1)).toBe(true);
expect(notificationText.includes(P3)).toBe(true);
await p2.getNotifications().closeLobbyParticipantAccessDenied();
const { p3 } = ctx;
// check the denied one is out of lobby, sees the notification about it
await p3.getNotifications().waitForLobbyAccessDeniedNotification();
expect(await p3.getLobbyScreen().isLobbyRoomJoined()).toBe(false);
await p3.hangup();
});
it('approve from participants pane', async () => {
const { p1 } = ctx;
const knockingParticipant = await enterLobby(p1, false);
// moderator allows access
const p1ParticipantsPane = p1.getParticipantsPane();
await p1ParticipantsPane.open();
await p1ParticipantsPane.admitLobbyParticipant(knockingParticipant);
await p1ParticipantsPane.close();
const { p3 } = ctx;
// ensure 3 participants in the call will check for the third one that muc is joined, ice connected,
// media is being receiving and there are two remote streams
await p3.waitToJoinMUC();
await p3.waitForIceConnected();
await p3.waitForSendReceiveData();
await p3.waitForRemoteStreams(2);
// now check third one display name in the room, is the one set in the prejoin screen
// now check third one display name in the room, is the one set in the prejoin screen
const name = await p1.getFilmstrip().getRemoteDisplayName(await p3.getEndpointId());
expect(name).toBe(P3);
await p3.hangup();
});
it('reject from participants pane', async () => {
const { p1 } = ctx;
const knockingParticipant = await enterLobby(p1, false);
// moderator rejects access
const p1ParticipantsPane = p1.getParticipantsPane();
await p1ParticipantsPane.open();
await p1ParticipantsPane.rejectLobbyParticipant(knockingParticipant);
await p1ParticipantsPane.close();
const { p3 } = ctx;
// check the denied one is out of lobby, sees the notification about it
// The third participant should see a warning that his access to the room was denied
await p3.getNotifications().waitForLobbyAccessDeniedNotification();
// check Lobby room not left
expect(await p3.getLobbyScreen().isLobbyRoomJoined()).toBe(false);
await p3.hangup();
});
it('lobby user leave', async () => {
const { p1 } = ctx;
await enterLobby(p1, false);
await ctx.p3.hangup();
// check that moderator (participant 1) no longer sees notification about participant in lobby
await p1.getNotifications().waitForHideOfKnockingParticipants();
});
it('lobby persistence', async () => {
const { p1, p2 } = ctx;
await enterLobby(p1, false);
const { p3 } = ctx;
expect(await p3.getLobbyScreen().isLobbyRoomJoined()).toBe(true);
await p1.hangup();
await p2.hangup();
await p3.driver.$('.dialog.leaveReason').isExisting();
await p3.driver.waitUntil(
async () => !await p3.getLobbyScreen().isLobbyRoomJoined(),
{
timeout: 2000,
timeoutMsg: 'p3 did not leave lobby'
}
);
await p3.hangup();
});
it('disable while participant in lobby', async () => {
await ensureTwoParticipants();
const { p1 } = ctx;
await enableLobby();
await enterLobby(p1);
const p1SecurityDialog = p1.getSecurityDialog();
await p1.getToolbar().clickSecurityButton();
await p1SecurityDialog.waitForDisplay();
await p1SecurityDialog.toggleLobby();
await p1SecurityDialog.waitForLobbyEnabled(true);
const { p3 } = ctx;
await p3.waitToJoinMUC();
expect(await p3.getLobbyScreen().isLobbyRoomJoined()).toBe(false);
});
it('change of moderators in lobby', async () => {
// The test below is only correct when the environment is configured to automatically elect a new moderator
// when the moderator leaves. For environments where this is not the case, the test is skipped.
if (!expectations.autoModerator) {
return;
}
await hangupAllParticipants();
await ensureTwoParticipants();
const { p1, p2 } = ctx;
// hanging up the first one, which is moderator and second one should be
await p1.hangup();
await p2.driver.waitUntil(
() => p2.isModerator(),
{
timeout: 3000,
timeoutMsg: 'p2 is not moderator after p1 leaves'
}
);
const p2SecurityDialog = p2.getSecurityDialog();
await p2.getToolbar().clickSecurityButton();
await p2SecurityDialog.waitForDisplay();
await p2SecurityDialog.toggleLobby();
await p2SecurityDialog.waitForLobbyEnabled();
// here the important check is whether the moderator sees the knocking participant
await enterLobby(p2, false);
});
it('shared password', async () => {
await hangupAllParticipants();
await ensureTwoParticipants();
const { p1 } = ctx;
await enableLobby();
const p1SecurityDialog = p1.getSecurityDialog();
await p1.getToolbar().clickSecurityButton();
await p1SecurityDialog.waitForDisplay();
expect(await p1SecurityDialog.isLocked()).toBe(false);
const roomPasscode = String(Math.trunc(Math.random() * 1_000_000));
await p1SecurityDialog.addPassword(roomPasscode);
await p1.driver.waitUntil(
() => p1SecurityDialog.isLocked(),
{
timeout: 2000,
timeoutMsg: 'room did not lock for p1'
}
);
await enterLobby(p1, false);
const { p3 } = ctx;
// now fill in password
const lobbyScreen = p3.getLobbyScreen();
await lobbyScreen.enterPassword(roomPasscode);
await p3.waitToJoinMUC();
await p3.waitForIceConnected();
await p3.waitForSendReceiveData();
});
it('enable with more than two participants', async () => {
await hangupAllParticipants();
await ensureThreeParticipants();
await enableLobby();
// we need to check remote participants as isInMuc has not changed its value as
// the bug is triggered by presence with status 322 which is not handled correctly
const { p1, p2, p3 } = ctx;
await p1.waitForRemoteStreams(2);
await p2.waitForRemoteStreams(2);
await p3.waitForRemoteStreams(2);
});
it('moderator leaves while lobby enabled', async () => {
// The test below is only correct when the environment is configured to automatically elect a new moderator
// when the moderator leaves. For environments where this is not the case, the test is skipped.
if (!expectations.autoModerator) {
return;
}
const { p1, p2, p3 } = ctx;
await p3.hangup();
await p1.hangup();
await p2.driver.waitUntil(
() => p2.isModerator(),
{
timeout: 3000,
timeoutMsg: 'p2 is not moderator after p1 leaves'
}
);
const lobbyScreen = p2.getLobbyScreen();
expect(await lobbyScreen.isLobbyRoomJoined()).toBe(true);
});
it('reject and approve in pre-join', async () => {
await hangupAllParticipants();
await ensureTwoParticipants();
await enableLobby();
const { p1, p2 } = ctx;
const knockingParticipant = await enterLobby(p1, true, true);
// moderator rejects access
const p1ParticipantsPane = p1.getParticipantsPane();
await p1ParticipantsPane.open();
await p1ParticipantsPane.rejectLobbyParticipant(knockingParticipant);
await p1ParticipantsPane.close();
const { p3 } = ctx;
// check the denied one is out of lobby, sees the notification about it
// The third participant should see a warning that his access to the room was denied
await p3.getNotifications().waitForLobbyAccessDeniedNotification();
// check Lobby room left
expect(await p3.getLobbyScreen().isLobbyRoomJoined()).toBe(false);
// try again entering the lobby with the third one and approve it
// check that everything is fine in the meeting
await p3.getNotifications().closeLocalLobbyAccessDenied();
// let's retry to enter the lobby and approve this time
const lobbyScreen = p3.getPreJoinScreen();
// click join button
await lobbyScreen.getJoinButton().click();
await lobbyScreen.waitToJoinLobby();
// check that moderator (participant 1) sees notification about participant in lobby
const name = await p1.getNotifications().getKnockingParticipantName();
expect(name).toBe(P3);
expect(await lobbyScreen.isLobbyRoomJoined()).toBe(true);
await p1ParticipantsPane.open();
await p1ParticipantsPane.admitLobbyParticipant(knockingParticipant);
await p1ParticipantsPane.close();
await p3.waitForParticipants(2);
await p3.waitForRemoteStreams(2);
expect(await p3.getFilmstrip().countVisibleThumbnails()).toBe(3);
// Check that there are no gaps in the filmstrip after joining from lobby
await p1.getFilmstrip().assertNoGapsInFilmstrip();
await p2.getFilmstrip().assertNoGapsInFilmstrip();
await p3.getFilmstrip().assertNoGapsInFilmstrip();
});
});
/**
* Enable lobby and check that it is enabled.
*/
async function enableLobby() {
const { p1, p2 } = ctx;
const p1SecurityDialog = p1.getSecurityDialog();
await p1.getToolbar().clickSecurityButton();
await p1SecurityDialog.waitForDisplay();
expect(await p1SecurityDialog.isLobbyEnabled()).toBe(false);
await p1SecurityDialog.toggleLobby();
await p1SecurityDialog.waitForLobbyEnabled();
expect((await p2.getNotifications().getLobbyEnabledText()).includes(p1.name)).toBe(true);
await p2.getNotifications().closeLobbyEnabled();
const p2SecurityDialog = p2.getSecurityDialog();
await p2.getToolbar().clickSecurityButton();
await p2SecurityDialog.waitForDisplay();
// lobby is visible to moderators only, this depends on whether deployment is all moderators or not
if (await p2.isModerator()) {
await p2SecurityDialog.waitForLobbyEnabled();
} else {
expect(await p2SecurityDialog.isLobbySectionPresent()).toBe(false);
}
// let's close the security dialog, or we will not be able to click
// on popups for allow/deny participants
await p1SecurityDialog.clickCloseButton();
await p2SecurityDialog.clickCloseButton();
}
/**
* Expects that lobby is enabled for the room we will try to join.
* Lobby UI is shown, enter display name and join.
* Checks Lobby UI and also that when joining the moderator sees corresponding notifications.
*
* @param participant The participant that is moderator in the meeting.
* @param enterDisplayName whether to enter display name. We need to enter display name only the first time when
* a participant sees the lobby screen, next time visiting the page display name will be pre-filled
* from local storage.
* @param usePreJoin
* @return the participant name knocking.
*/
async function enterLobby(participant: Participant, enterDisplayName = false, usePreJoin = false) {
const options: IJoinOptions = { };
if (usePreJoin) {
options.configOverwrite = {
prejoinConfig: {
enabled: true
}
};
}
await ensureThreeParticipants({
...options,
skipDisplayName: true,
skipWaitToJoin: true,
skipInMeetingChecks: true
});
const { p3 } = ctx;
let screen: PreMeetingScreen;
// WebParticipant participant3 = getParticipant3();
// ParentPreMeetingScreen lobbyScreen;
if (usePreJoin) {
screen = p3.getPreJoinScreen();
} else {
screen = p3.getLobbyScreen();
}
// participant 3 should be now on pre-join screen
await screen.waitForLoading();
const displayNameInput = screen.getDisplayNameInput();
// check display name is visible
expect(await displayNameInput.isExisting()).toBe(true);
expect(await displayNameInput.isDisplayed()).toBe(true);
const joinButton = screen.getJoinButton();
expect(await joinButton.isExisting()).toBe(true);
if (enterDisplayName) {
let classes = await joinButton.getAttribute('class');
if (!usePreJoin) {
// check join button is disabled
expect(classes.includes('disabled')).toBe(true);
}
// TODO check that password is hidden as the room does not have password
// this check needs to be added once the functionality exists
// enter display name
await screen.enterDisplayName(P3);
// check join button is enabled
classes = await joinButton.getAttribute('class');
expect(classes.includes('disabled')).toBe(false);
}
// click join button
await screen.getJoinButton().click();
await screen.waitToJoinLobby();
// check no join button
await p3.driver.waitUntil(
async () => !await joinButton.isExisting() || !await joinButton.isDisplayed() || !await joinButton.isEnabled(),
{
timeout: 2_000,
timeoutMsg: 'Join button is still available for p3'
});
// new screen, is password button shown
const passwordButton = screen.getPasswordButton();
expect(await passwordButton.isExisting()).toBe(true);
expect(await passwordButton.isEnabled()).toBe(true);
// check that moderator (participant 1) sees notification about participant in lobby
const name = await participant.getNotifications().getKnockingParticipantName();
expect(name).toBe(P3);
expect(await screen.isLobbyRoomJoined()).toBe(true);
return name;
}

View File

@@ -1,183 +0,0 @@
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { expectations } from '../../helpers/expectations';
import { ensureOneParticipant, ensureTwoParticipants, joinSecondParticipant } from '../../helpers/participants';
import type SecurityDialog from '../../pageobjects/SecurityDialog';
setTestProperties(__filename, {
description: '1. Set a room password (assert the image changes to locked). \
2. Join with a second participant. \
3. Assert password is required (and padlock is locked). \
4. Assert wrong password fails. \
5. Unlock the room (assert the padlock is unlocked) \
6. Assert room is unlocked and the padlock is unlocked.',
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Lock room', () => {
let p1: Participant, p2: Participant;
let roomKey: string;
it('setup', async () => {
if (!expectations.moderation.setPasswordAvailable) {
ctx.skipSuiteTests = 'setPasswordAvailable is not expected to be available';
return;
}
await ensureOneParticipant();
p1 = ctx.p1;
roomKey = `${Math.trunc(Math.random() * 1_000_000)}`;
await setPassword(p1, roomKey);
});
it('enter participant in locked room', async () => {
// first enter wrong pin then correct one
await joinSecondParticipant({
skipWaitToJoin: true
});
p2 = ctx.p2;
const p2PasswordDialog = p2.getPasswordDialog();
// Submit a wrong password
await p2PasswordDialog.waitForDialog();
await p2PasswordDialog.submitPassword(`${roomKey}1234`);
// give some time to the password prompt to disappear and send the password
// TODO: wait until the dialog is not displayed? Assert the room is not joined?
await p2.driver.pause(500);
// Submit the correct password
await p2PasswordDialog.waitForDialog();
await p2PasswordDialog.submitPassword(roomKey);
await p2.waitToJoinMUC();
const p2SecurityDialog = p2.getSecurityDialog();
await p2.getToolbar().clickSecurityButton();
await p2SecurityDialog.waitForDisplay();
await waitForRoomLockState(p2SecurityDialog, true);
});
it('unlock room', async () => {
await p2.hangup();
await removePassword(p1);
});
it('join the unlocked room', async () => {
// Just enter the room and check that is not locked.
await ensureTwoParticipants();
p2 = ctx.p2;
const p2SecurityDialog = p2.getSecurityDialog();
await p2.getToolbar().clickSecurityButton();
await p2SecurityDialog.waitForDisplay();
await waitForRoomLockState(p2SecurityDialog, false);
await p2SecurityDialog.clickCloseButton();
});
it('set password while participants are in the room', async () => {
// Both participants are in unlocked room, lock it and see whether the
// change is reflected on the second participant icon.
roomKey = `${Math.trunc(Math.random() * 1_000_000)}`;
await setPassword(p1, roomKey);
const p2SecurityDialog = p2.getSecurityDialog();
await p2.getToolbar().clickSecurityButton();
await p2SecurityDialog.waitForDisplay();
await waitForRoomLockState(p2SecurityDialog, true);
await removePassword(p1);
await waitForRoomLockState(p2SecurityDialog, false);
});
it('unlock after participant enter wrong password', async () => {
// P1 locks the room. Participant tries to enter using wrong password.
// P1 unlocks the room and Participant submits the password prompt with no password entered and
// should enter of unlocked room.
await p2.hangup();
roomKey = `${Math.trunc(Math.random() * 1_000_000)}`;
await setPassword(p1, roomKey);
await joinSecondParticipant({
skipWaitToJoin: true
});
p2 = ctx.p2;
// wait for password prompt
const p2PasswordDialog = p2.getPasswordDialog();
await p2PasswordDialog.waitForDialog();
await p2PasswordDialog.submitPassword(`${roomKey}1234`);
// give sometime to the password prompt to disappear and send the password
await p2.driver.pause(500);
// wait for password prompt
await p2PasswordDialog.waitForDialog();
await removePassword(p1);
await p2PasswordDialog.clickOkButton();
await p2.waitToJoinMUC();
const p2SecurityDialog = p2.getSecurityDialog();
await p2.getToolbar().clickSecurityButton();
await p2SecurityDialog.waitForDisplay();
await waitForRoomLockState(p2SecurityDialog, false);
});
});
/**
* Set a room password via the UI.
*/
async function setPassword(p: Participant, password: string) {
const securityDialog = p.getSecurityDialog();
await p.getToolbar().clickSecurityButton();
await securityDialog.waitForDisplay();
await waitForRoomLockState(securityDialog, false);
await securityDialog.addPassword(password);
await securityDialog.clickCloseButton();
await p.getToolbar().clickSecurityButton();
await securityDialog.waitForDisplay();
await waitForRoomLockState(securityDialog, true);
await securityDialog.clickCloseButton();
}
/**
* Remove the room password via the UI.
*/
async function removePassword(p: Participant) {
const securityDialog = p.getSecurityDialog();
await p.getToolbar().clickSecurityButton();
await securityDialog.waitForDisplay();
await securityDialog.removePassword();
await waitForRoomLockState(securityDialog, false);
await securityDialog.clickCloseButton();
}
/**
* Waits for the room to be locked or unlocked.
* @param securityDialog
* @param locked
*/
function waitForRoomLockState(securityDialog: SecurityDialog, locked: boolean) {
return securityDialog.participant.driver.waitUntil(
async () => await securityDialog.isLocked() === locked,
{
timeout: 3_000, // 3 seconds
timeoutMsg: `Timeout waiting for the room to unlock for ${securityDialog.participant.name}.`
}
);
}

View File

@@ -1,42 +0,0 @@
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { expectations } from '../../helpers/expectations';
import { joinMuc } from '../../helpers/joinMuc';
setTestProperties(__filename, {
description: 'This test asserts that participants have the expected role ("moderator" or not). Failures here\
most likely indicate that the environment or test framework is misconfigured.',
usesBrowsers: [ 'p1', 'p2' ]
});
// Just make sure that users are given moderator rights as specified in the expectations config.
describe('Moderation', () => {
let p1: Participant, p2: Participant;
it('setup', async () => {
p1 = await joinMuc({ name: 'p1', token: testsConfig.jwt.preconfiguredToken });
p2 = await joinMuc({ name: 'p2', token: testsConfig.jwt.preconfiguredToken });
});
it('first moderator', async () => {
if (expectations.moderation.firstModerator) {
expect(await p1.isModerator()).toBe(true);
} else {
expect(await p1.isModerator()).toBe(false);
}
});
it('all moderators', async () => {
if (expectations.moderation.allModerators) {
expect(await p1.isModerator()).toBe(true);
expect(await p2.isModerator()).toBe(true);
}
});
it('auto moderator promotion', async () => {
if (expectations.moderation.autoModerator && !expectations.moderation.allModerators) {
expect(await p1.isModerator()).toBe(true);
expect(await p2.isModerator()).toBe(false);
await p1.hangup();
await p2.driver.waitUntil(async () => (await p2.isModerator()));
}
});
});

View File

@@ -1,33 +0,0 @@
import { ensureOneParticipant } from '../../helpers/participants';
describe('Chat panel', () => {
it('join participant', () => ensureOneParticipant());
it('start closed', async () => {
expect(await ctx.p1.getChatPanel().isOpen()).toBe(false);
});
it('open', async () => {
const { p1 } = ctx;
await p1.getToolbar().clickChatButton();
expect(await p1.getChatPanel().isOpen()).toBe(true);
});
it('use shortcut to close', async () => {
const chatPanel = ctx.p1.getChatPanel();
await chatPanel.pressShortcut();
expect(await chatPanel.isOpen()).toBe(false);
});
it('use shortcut to open', async () => {
const chatPanel = ctx.p1.getChatPanel();
await chatPanel.pressShortcut();
expect(await chatPanel.isOpen()).toBe(true);
});
it('use button to open', async () => {
const { p1 } = ctx;
await p1.getToolbar().clickCloseChatButton();
expect(await p1.getChatPanel().isOpen()).toBe(false);
});
});

View File

@@ -1,46 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Display name', () => {
it('joining the meeting', () => ensureTwoParticipants({ skipDisplayName: true }));
it('check change', async () => {
const { p1, p2 } = ctx;
// default remote display name
const defaultDisplayName = await p1.execute(() => config.defaultRemoteDisplayName);
const p1EndpointId = await p1.getEndpointId();
const p2EndpointId = await p2.getEndpointId();
// Checks whether default display names are set and shown, when both sides still miss the display name.
expect(await p1.getFilmstrip().getRemoteDisplayName(p2EndpointId)).toBe(defaultDisplayName);
expect(await p2.getFilmstrip().getRemoteDisplayName(p1EndpointId)).toBe(defaultDisplayName);
const randomName = `Name${Math.trunc(Math.random() * 1_000_000)}`;
await p2.setLocalDisplayName(randomName);
expect(await p2.getLocalDisplayName()).toBe(randomName);
expect(await p1.getFilmstrip().getRemoteDisplayName(p2EndpointId)).toBe(randomName);
});
it('check persistence', async () => {
const { p2 } = ctx;
const randomName = `Name${Math.trunc(Math.random() * 1_000_000)}`;
await p2.setLocalDisplayName(randomName);
expect(await p2.getLocalDisplayName()).toBe(randomName);
await p2.hangup();
await ensureTwoParticipants({
skipDisplayName: true
});
expect(await p2.getLocalDisplayName()).toBe(randomName);
});
});

View File

@@ -1,25 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Hangup', () => {
it('joining the meeting', () => ensureTwoParticipants());
it('hangup call and check', async () => {
const { p1 } = ctx;
const url = await p1.driver.getUrl();
await p1.getToolbar().clickHangupButton();
await p1.driver.waitUntil(
async () => await p1.driver.getUrl() !== url,
{
timeout: 5000,
timeoutMsg: 'p1 did not navigate away from the conference'
}
);
});
});

View File

@@ -1,63 +0,0 @@
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { expectations } from '../../helpers/expectations';
import { joinMuc } from '../../helpers/joinMuc';
setTestProperties(__filename, {
description: ' Tests that the digits only password feature works. When the roomPasswordNumberOfDigits config \
option is set, the UI should only allow setting the password to a string of digits (with the given length).'
});
describe('Lock room with digits only', () => {
let p: Participant;
it('setup', async () => {
if (!expectations.moderation.setPasswordAvailable) {
ctx.skipSuiteTests = 'setPasswordAvailable is not expected to be available';
return;
}
p = await joinMuc({
name: 'p1',
token: testsConfig.jwt.preconfiguredToken
}, {
configOverwrite: {
roomPasswordNumberOfDigits: 5
}
});
});
it('config value', async () => {
expect(await p.execute(
() => APP.store.getState()['features/base/config'].roomPasswordNumberOfDigits)).toBe(5);
});
it('set an invalid password', async () => {
const securityDialog = p.getSecurityDialog();
await p.getToolbar().clickSecurityButton();
await securityDialog.waitForDisplay();
expect(await securityDialog.isLocked()).toBe(false);
// Set a non-numeric password.
await securityDialog.addPassword('AAAAA');
expect(await securityDialog.isLocked()).toBe(false);
await securityDialog.clickCloseButton();
});
it('set a valid password', async () => {
const securityDialog = p.getSecurityDialog();
await p.getToolbar().clickSecurityButton();
await securityDialog.waitForDisplay();
await securityDialog.addPassword('12345');
await securityDialog.clickCloseButton();
await p.getToolbar().clickSecurityButton();
await securityDialog.waitForDisplay();
expect(await securityDialog.isLocked()).toBe(true);
});
});

View File

@@ -1,86 +0,0 @@
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import {
ensureThreeParticipants,
ensureTwoParticipants
} from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2', 'p3' ]
});
const ONE_ON_ONE_CONFIG_OVERRIDES = {
configOverwrite: {
disable1On1Mode: false,
toolbarConfig: {
timeout: 500,
alwaysVisible: false
}
}
};
describe('One-on-one (1on1) mode', () => {
it('filmstrip hidden in 1on1', async () => {
await ensureTwoParticipants(ONE_ON_ONE_CONFIG_OVERRIDES);
const { p1, p2 } = ctx;
await configureToolbarsToHideQuickly(p1);
await configureToolbarsToHideQuickly(p2);
await p1.getFilmstrip().verifyRemoteVideosDisplay(false);
await p2.getFilmstrip().verifyRemoteVideosDisplay(false);
});
it('filmstrip visible with more than 2', async () => {
await ensureThreeParticipants(ONE_ON_ONE_CONFIG_OVERRIDES);
const { p1, p2, p3 } = ctx;
await configureToolbarsToHideQuickly(p3);
await p1.getFilmstrip().verifyRemoteVideosDisplay(true);
await p2.getFilmstrip().verifyRemoteVideosDisplay(true);
await p3.getFilmstrip().verifyRemoteVideosDisplay(true);
});
it('filmstrip display when returning to 1on1', async () => {
const { p1, p2, p3 } = ctx;
await p2.getFilmstrip().pinParticipant(p2);
await p3.hangup();
await p1.getFilmstrip().verifyRemoteVideosDisplay(false);
await p2.getFilmstrip().verifyRemoteVideosDisplay(true);
});
it('filmstrip visible on self view focus', async () => {
const { p1 } = ctx;
await p1.getFilmstrip().pinParticipant(p1);
await p1.getFilmstrip().verifyRemoteVideosDisplay(true);
await p1.getFilmstrip().unpinParticipant(p1);
await p1.getFilmstrip().verifyRemoteVideosDisplay(false);
});
it('filmstrip hover show videos', async () => {
const { p1 } = ctx;
await p1.getFilmstrip().hoverOverLocalVideo();
await p1.getFilmstrip().verifyRemoteVideosDisplay(true);
});
});
/**
* Hangs up all participants (p1, p2 and p3)
* @returns {Promise<void>}
*/
function configureToolbarsToHideQuickly(participant: Participant): Promise<void> {
return participant.execute(() => {
APP.UI.dockToolbar(false);
APP.UI.showToolbar(250);
});
}

View File

@@ -1,126 +0,0 @@
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureOneParticipant, joinFirstParticipant, joinSecondParticipant } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Pre-join screen', () => {
it('display name required', async () => {
await joinFirstParticipant({
configOverwrite: {
prejoinConfig: {
enabled: true,
},
requireDisplayName: true
},
skipDisplayName: true,
skipWaitToJoin: true
});
const p1PreJoinScreen = ctx.p1.getPreJoinScreen();
await p1PreJoinScreen.waitForLoading();
const joinButton = p1PreJoinScreen.getJoinButton();
await joinButton.waitForDisplayed();
await joinButton.click();
const error = p1PreJoinScreen.getErrorOnJoin();
await error.waitForDisplayed();
await ctx.p1.hangup();
});
it('without lobby', async () => {
await joinFirstParticipant({
configOverwrite: {
prejoinConfig: {
enabled: true,
}
},
skipDisplayName: true,
skipWaitToJoin: true
});
const p1PreJoinScreen = ctx.p1.getPreJoinScreen();
await p1PreJoinScreen.waitForLoading();
const joinButton = p1PreJoinScreen.getJoinButton();
await joinButton.waitForDisplayed();
await ctx.p1.hangup();
});
it('without audio', async () => {
await joinFirstParticipant({
configOverwrite: {
prejoinConfig: {
enabled: true,
}
},
skipDisplayName: true,
skipWaitToJoin: true
});
const { p1 } = ctx;
const p1PreJoinScreen = p1.getPreJoinScreen();
await p1PreJoinScreen.waitForLoading();
await p1PreJoinScreen.getJoinOptions().click();
const joinWithoutAudioBtn = p1PreJoinScreen.getJoinWithoutAudioButton();
await joinWithoutAudioBtn.waitForClickable();
await joinWithoutAudioBtn.click();
await p1.waitToJoinMUC();
await p1.driver.$('//div[contains(@class, "audio-preview")]//div[contains(@class, "toolbox-icon") '
+ 'and contains(@class, "toggled") and contains(@class, "disabled")]')
.waitForDisplayed();
await ctx.p1.hangup();
});
it('with lobby', async () => {
await ensureOneParticipant();
const { p1 } = ctx;
const p1SecurityDialog = p1.getSecurityDialog();
await p1.getToolbar().clickSecurityButton();
await p1SecurityDialog.waitForDisplay();
expect(await p1SecurityDialog.isLobbyEnabled()).toBe(false);
await p1SecurityDialog.toggleLobby();
await p1SecurityDialog.waitForLobbyEnabled();
await joinSecondParticipant({
configOverwrite: {
prejoinConfig: {
enabled: true,
}
},
skipDisplayName: true,
skipWaitToJoin: true
});
const p1PreJoinScreen = ctx.p2.getPreJoinScreen();
await p1PreJoinScreen.waitForLoading();
const joinButton = p1PreJoinScreen.getJoinButton();
await joinButton.waitForDisplayed();
});
});

View File

@@ -1,73 +0,0 @@
import type { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Self view', () => {
it('joining the meeting', () => ensureTwoParticipants());
it('hide from menu', async () => {
const { p1 } = ctx;
await checkSelfViewHidden(p1, false);
await p1.getFilmstrip().hideSelfView();
await checkSelfViewHidden(p1, true, true);
await p1.getToolbar().clickEnterTileViewButton();
await checkSelfViewHidden(p1, true);
});
it('show from settings', async () => {
const { p1 } = ctx;
await toggleSelfViewFromSettings(p1, false);
await checkSelfViewHidden(p1, false);
});
it('hide from settings', async () => {
const { p1 } = ctx;
await toggleSelfViewFromSettings(p1, true);
await checkSelfViewHidden(p1, true, true);
});
it('check in alone meeting', async () => {
const { p1, p2 } = ctx;
await checkSelfViewHidden(p1, true);
await p2.hangup();
await checkSelfViewHidden(p1, true);
});
});
/**
* Toggles the self view option from the settings dialog.
*/
async function toggleSelfViewFromSettings(participant: Participant, hide: boolean) {
await participant.getToolbar().clickSettingsButton();
const settings = participant.getSettingsDialog();
await settings.waitForDisplay();
await settings.setHideSelfView(hide);
await settings.submit();
}
/**
* Checks whether the local self view is displayed or not.
*/
async function checkSelfViewHidden(participant: Participant, hidden: boolean, checkNotification = false) {
if (checkNotification) {
await participant.getNotifications().waitForReEnableSelfViewNotification();
await participant.getNotifications().closeReEnableSelfViewNotification();
}
await participant.getFilmstrip().assertSelfViewIsHidden(hidden);
}

View File

@@ -1,58 +0,0 @@
import { Participant } from '../../helpers/Participant';
import { setTestProperties } from '../../helpers/TestProperties';
import { config as testsConfig } from '../../helpers/TestsConfig';
import { joinMuc } from '../../helpers/joinMuc';
/**
* The CSS selector for local video when outside of tile view. It should
* be in a container separate from remote videos so remote videos can
* scroll while local video stays docked.
*/
const FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR = '#filmstripLocalVideo #localVideoContainer';
/**
* The CSS selector for local video tile view is enabled. It should display
* at the end of all the other remote videos, as the last tile.
*/
const TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR = '.remote-videos #localVideoContainer';
setTestProperties(__filename, {
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Tile view', () => {
let p1: Participant, p2: Participant;
before('join the meeting', async () => {
p1 = await joinMuc({ name: 'p1', token: testsConfig.jwt.preconfiguredToken });
p2 = await joinMuc({ name: 'p2' });
});
it('entering tile view', async () => {
await p1.getToolbar().clickEnterTileViewButton();
await p1.waitForTileViewDisplayed();
});
it('exit tile view by pinning', async () => {
await p1.getFilmstrip().pinParticipant(p2);
await p1.waitForTileViewDisplayed(true);
});
it('local video is displayed in tile view', async () => {
await p1.getToolbar().clickEnterTileViewButton();
await p1.waitForTileViewDisplayed();
await p1.driver.$(TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({ timeout: 3000 });
await p1.driver.$(FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({
timeout: 3000,
reverse: true
});
});
it('exit tile view by clicking "exit tile view"', async () => {
await p1.getToolbar().clickExitTileViewButton();
await p1.waitForTileViewDisplayed(true);
});
it('local video display independently from remote', async () => {
await p1.driver.$(TILE_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({
timeout: 3000,
reverse: true
});
await p1.driver.$(FILMSTRIP_VIEW_LOCAL_VIDEO_CSS_SELECTOR).waitForDisplayed({ timeout: 3000 });
});
});

View File

@@ -1,27 +0,0 @@
import { ensureOneParticipant } from '../../helpers/participants';
describe('Video layout', () => {
it('join participant', () => ensureOneParticipant());
it('check', async () => {
const { p1 } = ctx;
const innerWidth = parseInt(await p1.execute('return window.innerWidth'), 10);
const innerHeight = parseInt(await p1.execute('return window.innerHeight'), 10);
const largeVideo = p1.driver.$('//div[@id="largeVideoContainer"]');
const filmstrip = p1.driver.$('//div[contains(@class, "filmstrip")]');
let filmstripWidth;
if (!await filmstrip.isExisting() || !await filmstrip.isDisplayed()) {
filmstripWidth = 0;
} else {
filmstripWidth = await filmstrip.getSize('width');
}
const largeVideoSize = await largeVideo.getSize();
expect((largeVideoSize.width === (innerWidth - filmstripWidth)) || (largeVideoSize.height === innerHeight))
.toBe(true);
});
});