This commit is contained in:
86
tests/specs/2way/audioOnly.spec.ts
Normal file
86
tests/specs/2way/audioOnly.spec.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('Audio only', () => {
|
||||
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);
|
||||
}
|
||||
41
tests/specs/2way/displayName.spec.ts
Normal file
41
tests/specs/2way/displayName.spec.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('DisplayName', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
20
tests/specs/2way/endConference.spec.ts
Normal file
20
tests/specs/2way/endConference.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('End Conference', () => {
|
||||
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'
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
52
tests/specs/2way/fakeDialInAudio.spec.ts
Normal file
52
tests/specs/2way/fakeDialInAudio.spec.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import process from 'node:process';
|
||||
|
||||
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
|
||||
import { cleanup, isDialInEnabled, waitForAudioFromDialInParticipant } from '../helpers/DialIn';
|
||||
|
||||
describe('Fake Dial-In', () => {
|
||||
it('join participant', async () => {
|
||||
// we execute fake dial in only if the real dial in is not enabled
|
||||
|
||||
// check rest url is not configured
|
||||
if (process.env.DIAL_IN_REST_URL) {
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await ensureOneParticipant();
|
||||
|
||||
// check dial-in is enabled, so skip
|
||||
if (await isDialInEnabled(ctx.p1)) {
|
||||
ctx.skipSuiteTests = true;
|
||||
}
|
||||
});
|
||||
|
||||
it('open invite dialog', async () => {
|
||||
await ctx.p1.getInviteDialog().open();
|
||||
|
||||
await ctx.p1.getInviteDialog().clickCloseButton();
|
||||
});
|
||||
|
||||
it('invite second participant', async () => {
|
||||
if (!await ctx.p1.isInMuc()) {
|
||||
// local participant did not join abort
|
||||
return;
|
||||
}
|
||||
|
||||
await ensureTwoParticipants();
|
||||
});
|
||||
|
||||
it('wait for audio from second participant', async () => {
|
||||
const { p1 } = ctx;
|
||||
|
||||
if (!await p1.isInMuc()) {
|
||||
// local participant did not join abort
|
||||
return;
|
||||
}
|
||||
|
||||
await waitForAudioFromDialInParticipant(p1);
|
||||
|
||||
await cleanup(p1);
|
||||
});
|
||||
});
|
||||
42
tests/specs/2way/grantModerator.spec.ts
Normal file
42
tests/specs/2way/grantModerator.spec.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('Grant moderator', () => {
|
||||
it('joining the meeting', async () => {
|
||||
await ensureOneParticipant();
|
||||
|
||||
if (await ctx.p1.execute(() => typeof APP.conference._room.grantOwner !== 'function')) {
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await ensureTwoParticipants();
|
||||
});
|
||||
|
||||
it('grant moderator and validate', async () => {
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
if (!await p1.isModerator()) {
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (await p2.isModerator()) {
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await p1.getFilmstrip().grantModerator(p2);
|
||||
|
||||
await p2.driver.waitUntil(
|
||||
() => p2.isModerator(),
|
||||
{
|
||||
timeout: 3000,
|
||||
timeoutMsg: 'p2 did not become moderator'
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
});
|
||||
44
tests/specs/2way/kick.spec.ts
Normal file
44
tests/specs/2way/kick.spec.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('Kick', () => {
|
||||
it('joining the meeting', async () => {
|
||||
await ensureTwoParticipants();
|
||||
|
||||
if (!await ctx.p1.isModerator()) {
|
||||
ctx.skipSuiteTests = true;
|
||||
}
|
||||
});
|
||||
|
||||
it('kick and check', () => kickParticipant2AndCheck());
|
||||
|
||||
it('kick p2p and check', async () => {
|
||||
await ensureTwoParticipants({
|
||||
configOverwrite: {
|
||||
p2p: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await kickParticipant2AndCheck();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Kicks the second participant and checks that the participant is removed from the conference and that dialog is open.
|
||||
*/
|
||||
async function kickParticipant2AndCheck() {
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
await p1.getFilmstrip().kickParticipant(await p2.getEndpointId());
|
||||
|
||||
await p1.waitForParticipants(0);
|
||||
|
||||
// check that the kicked participant sees the kick reason dialog
|
||||
// let's wait for this to appear at least 2 seconds
|
||||
await p2.driver.waitUntil(
|
||||
async () => p2.isLeaveReasonDialogOpen(), {
|
||||
timeout: 2000,
|
||||
timeoutMsg: 'No leave reason dialog shown for p2'
|
||||
});
|
||||
}
|
||||
189
tests/specs/2way/lockRoom.spec.ts
Normal file
189
tests/specs/2way/lockRoom.spec.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { ensureOneParticipant, ensureTwoParticipants, joinSecondParticipant } from '../../helpers/participants';
|
||||
import type SecurityDialog from '../../pageobjects/SecurityDialog';
|
||||
|
||||
let roomKey: string;
|
||||
|
||||
/**
|
||||
* 1. Lock the room (make sure the image changes to locked)
|
||||
* 2. Join with a second browser/tab
|
||||
* 3. Make sure we are required to enter a password.
|
||||
* (Also make sure the padlock is locked)
|
||||
* 4. Enter wrong password, make sure we are not joined in the room
|
||||
* 5. Unlock the room (Make sure the padlock is unlocked)
|
||||
* 6. Join again and make sure we are not asked for a password and that
|
||||
* the padlock is unlocked.
|
||||
*/
|
||||
describe('Lock Room', () => {
|
||||
it('joining the meeting', () => ensureOneParticipant());
|
||||
|
||||
it('locks the room', () => participant1LockRoom());
|
||||
|
||||
it('enter participant in locked room', async () => {
|
||||
// first enter wrong pin then correct one
|
||||
await joinSecondParticipant({
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: true
|
||||
});
|
||||
|
||||
const { p2 } = ctx;
|
||||
|
||||
// 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 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 () => {
|
||||
// Unlock room. Check whether room is still locked. Click remove and check whether it is unlocked.
|
||||
await ctx.p2.hangup();
|
||||
|
||||
await participant1UnlockRoom();
|
||||
});
|
||||
|
||||
it('enter participant in unlocked room', async () => {
|
||||
// Just enter the room and check that is not locked.
|
||||
// if we fail to unlock the room this one will detect it
|
||||
// as participant will fail joining
|
||||
await ensureTwoParticipants();
|
||||
|
||||
const { p2 } = ctx;
|
||||
const p2SecurityDialog = p2.getSecurityDialog();
|
||||
|
||||
await p2.getToolbar().clickSecurityButton();
|
||||
await p2SecurityDialog.waitForDisplay();
|
||||
|
||||
await waitForRoomLockState(p2SecurityDialog, false);
|
||||
|
||||
await p2SecurityDialog.clickCloseButton();
|
||||
});
|
||||
|
||||
it('update locked state while participants in room', async () => {
|
||||
// Both participants are in unlocked room, lock it and see whether the
|
||||
// change is reflected on the second participant icon.
|
||||
await participant1LockRoom();
|
||||
|
||||
const { p2 } = ctx;
|
||||
const p2SecurityDialog = p2.getSecurityDialog();
|
||||
|
||||
await p2.getToolbar().clickSecurityButton();
|
||||
await p2SecurityDialog.waitForDisplay();
|
||||
|
||||
await waitForRoomLockState(p2SecurityDialog, true);
|
||||
|
||||
await participant1UnlockRoom();
|
||||
|
||||
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 ctx.p2.hangup();
|
||||
await participant1LockRoom();
|
||||
await joinSecondParticipant({
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: true
|
||||
});
|
||||
|
||||
const { p2 } = ctx;
|
||||
|
||||
// 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 participant1UnlockRoom();
|
||||
|
||||
await p2PasswordDialog.clickOkButton();
|
||||
await p2.waitToJoinMUC();
|
||||
|
||||
const p2SecurityDialog = p2.getSecurityDialog();
|
||||
|
||||
await p2.getToolbar().clickSecurityButton();
|
||||
await p2SecurityDialog.waitForDisplay();
|
||||
|
||||
await waitForRoomLockState(p2SecurityDialog, false);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Participant1 locks the room.
|
||||
*/
|
||||
async function participant1LockRoom() {
|
||||
roomKey = `${Math.trunc(Math.random() * 1_000_000)}`;
|
||||
|
||||
const { p1 } = ctx;
|
||||
const p1SecurityDialog = p1.getSecurityDialog();
|
||||
|
||||
await p1.getToolbar().clickSecurityButton();
|
||||
await p1SecurityDialog.waitForDisplay();
|
||||
|
||||
await waitForRoomLockState(p1SecurityDialog, false);
|
||||
|
||||
await p1SecurityDialog.addPassword(roomKey);
|
||||
|
||||
await p1SecurityDialog.clickCloseButton();
|
||||
|
||||
await p1.getToolbar().clickSecurityButton();
|
||||
await p1SecurityDialog.waitForDisplay();
|
||||
|
||||
await waitForRoomLockState(p1SecurityDialog, true);
|
||||
|
||||
await p1SecurityDialog.clickCloseButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Participant1 unlocks the room.
|
||||
*/
|
||||
async function participant1UnlockRoom() {
|
||||
const { p1 } = ctx;
|
||||
const p1SecurityDialog = p1.getSecurityDialog();
|
||||
|
||||
await p1.getToolbar().clickSecurityButton();
|
||||
await p1SecurityDialog.waitForDisplay();
|
||||
|
||||
await p1SecurityDialog.removePassword();
|
||||
|
||||
await waitForRoomLockState(p1SecurityDialog, false);
|
||||
|
||||
await p1SecurityDialog.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}.`
|
||||
}
|
||||
);
|
||||
}
|
||||
138
tests/specs/2way/mute.spect.ts
Normal file
138
tests/specs/2way/mute.spect.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import type { Participant } from '../../helpers/Participant';
|
||||
import {
|
||||
checkForScreensharingTile,
|
||||
ensureOneParticipant,
|
||||
ensureTwoParticipants,
|
||||
joinSecondParticipant,
|
||||
muteAudioAndCheck,
|
||||
unmuteAudioAndCheck,
|
||||
unmuteVideoAndCheck
|
||||
} from '../../helpers/participants';
|
||||
|
||||
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());
|
||||
}
|
||||
125
tests/specs/2way/preJoin.spec.ts
Normal file
125
tests/specs/2way/preJoin.spec.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { ensureOneParticipant, joinFirstParticipant, joinSecondParticipant } from '../../helpers/participants';
|
||||
|
||||
describe('PreJoin', () => {
|
||||
it('display name required', async () => {
|
||||
await joinFirstParticipant({
|
||||
configOverwrite: {
|
||||
prejoinConfig: {
|
||||
enabled: true,
|
||||
},
|
||||
requireDisplayName: true
|
||||
},
|
||||
skipDisplayName: true,
|
||||
skipWaitToJoin: true,
|
||||
skipInMeetingChecks: 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,
|
||||
skipInMeetingChecks: 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,
|
||||
skipInMeetingChecks: 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,
|
||||
skipInMeetingChecks: true
|
||||
});
|
||||
|
||||
const p1PreJoinScreen = ctx.p2.getPreJoinScreen();
|
||||
|
||||
await p1PreJoinScreen.waitForLoading();
|
||||
|
||||
const joinButton = p1PreJoinScreen.getJoinButton();
|
||||
|
||||
await joinButton.waitForDisplayed();
|
||||
|
||||
});
|
||||
});
|
||||
68
tests/specs/2way/selfView.spec.ts
Normal file
68
tests/specs/2way/selfView.spec.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import type { Participant } from '../../helpers/Participant';
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
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);
|
||||
}
|
||||
29
tests/specs/2way/singlePort.spec.ts
Normal file
29
tests/specs/2way/singlePort.spec.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { Participant } from '../../helpers/Participant';
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('Single port', () => {
|
||||
it('joining the meeting', () => ensureTwoParticipants());
|
||||
|
||||
it('test', async () => {
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
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) : '';
|
||||
}
|
||||
41
tests/specs/2way/stopVideo.spec.ts
Normal file
41
tests/specs/2way/stopVideo.spec.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ensureTwoParticipants, muteVideoAndCheck, unmuteVideoAndCheck } from '../../helpers/participants';
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
32
tests/specs/2way/subject.spec.ts
Normal file
32
tests/specs/2way/subject.spec.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { Participant } from '../../helpers/Participant';
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
const MY_TEST_SUBJECT = 'My Test Subject';
|
||||
const SUBJECT_XPATH = '//div[starts-with(@class, "subject-text")]';
|
||||
|
||||
describe('Subject', () => {
|
||||
it('joining the meeting', () => ensureTwoParticipants({
|
||||
configOverwrite: {
|
||||
subject: MY_TEST_SUBJECT
|
||||
}
|
||||
}));
|
||||
|
||||
it('check', async () => {
|
||||
await checkSubject(ctx.p1, MY_TEST_SUBJECT);
|
||||
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);
|
||||
}
|
||||
39
tests/specs/2way/switchVideo.spec.ts
Normal file
39
tests/specs/2way/switchVideo.spec.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('SwitchVideo', () => {
|
||||
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();
|
||||
}
|
||||
26
tests/specs/2way/udp.spec.ts
Normal file
26
tests/specs/2way/udp.spec.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { Participant } from '../../helpers/Participant';
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('UDP', () => {
|
||||
it('joining the meeting', () => ensureTwoParticipants());
|
||||
|
||||
it('check', async () => {
|
||||
const { p1, p2 } = ctx;
|
||||
|
||||
// just in case wait 1500, this is the interval we use for `config.pcStatsInterval`
|
||||
await p1.driver.pause(1500);
|
||||
|
||||
expect(await getProtocol(p1)).toBe('udp');
|
||||
expect(await getProtocol(p2)).toBe('udp');
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
41
tests/specs/2way/urlNormalisation.spec.ts
Normal file
41
tests/specs/2way/urlNormalisation.spec.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { multiremotebrowser } from '@wdio/globals';
|
||||
|
||||
import { config } from '../../helpers/TestsConfig';
|
||||
import { ensureTwoParticipants } from '../../helpers/participants';
|
||||
|
||||
describe('URL Normalisation', () => {
|
||||
it('joining the meeting', async () => {
|
||||
|
||||
// if we are running with token this becomes ugly to match the URL
|
||||
if (config.jwt.preconfiguredToken) {
|
||||
ctx.skipSuiteTests = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// a hack to extract the baseUrl that the test will use
|
||||
const baseUrl = multiremotebrowser.getInstance('p1').options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
throw new Error('baseUrl is not set');
|
||||
}
|
||||
|
||||
await ensureTwoParticipants({
|
||||
forceTenant: 'tenant@example.com',
|
||||
roomName: `${ctx.roomName}@example.com`
|
||||
});
|
||||
});
|
||||
|
||||
it('check', async () => {
|
||||
const currentUrlStr = await ctx.p1.driver.getUrl();
|
||||
const currentUrl = new URL(currentUrlStr);
|
||||
const path = currentUrl.pathname;
|
||||
|
||||
const parts = path.split('/');
|
||||
|
||||
expect(parts[1]).toBe('tenantexample.com');
|
||||
|
||||
// @ts-ignore
|
||||
expect(parts[2]).toBe(`${ctx.roomName}example.com`);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user