diff --git a/package.json b/package.json index 78fbfa71e1..8478866814 100644 --- a/package.json +++ b/package.json @@ -226,6 +226,7 @@ "test-ff-single": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.firefox.conf.ts --spec", "test-ff": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.firefox.conf.ts", "test-dev": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.dev.conf.ts", + "test-dev-single": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.dev.conf.ts --spec", "test-grid": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.grid.conf.ts", "test-grid-single": "DOTENV_CONFIG_PATH=tests/.env wdio run tests/wdio.grid.conf.ts --spec" }, diff --git a/tests/helpers/Participant.ts b/tests/helpers/Participant.ts index 223403de1a..314f66f97a 100644 --- a/tests/helpers/Participant.ts +++ b/tests/helpers/Participant.ts @@ -15,6 +15,7 @@ import LargeVideo from '../pageobjects/LargeVideo'; import LobbyScreen from '../pageobjects/LobbyScreen'; import Notifications from '../pageobjects/Notifications'; import ParticipantsPane from '../pageobjects/ParticipantsPane'; +import PasswordDialog from '../pageobjects/PasswordDialog'; import PreJoinScreen from '../pageobjects/PreJoinScreen'; import SecurityDialog from '../pageobjects/SecurityDialog'; import SettingsDialog from '../pageobjects/SettingsDialog'; @@ -505,6 +506,13 @@ export class Participant { return new SettingsDialog(this); } + /** + * Returns the password dialog. + */ + getPasswordDialog(): PasswordDialog { + return new PasswordDialog(this); + } + /** * Returns the prejoin screen. */ diff --git a/tests/helpers/types.ts b/tests/helpers/types.ts index 8267dfb502..2193ca41fd 100644 --- a/tests/helpers/types.ts +++ b/tests/helpers/types.ts @@ -13,6 +13,7 @@ export type IContext = { p2: Participant; p3: Participant; p4: Participant; + roomKey: string; roomName: string; skipSuiteTests: boolean; times: any; diff --git a/tests/pageobjects/PasswordDialog.ts b/tests/pageobjects/PasswordDialog.ts new file mode 100644 index 0000000000..6ff77b28d8 --- /dev/null +++ b/tests/pageobjects/PasswordDialog.ts @@ -0,0 +1,37 @@ +import BaseDialog from './BaseDialog'; + +const INPUT_KEY_XPATH = '//input[@name="lockKey"]'; + +/** + * Represents the password dialog in a particular participant. + */ +export default class PasswordDialog extends BaseDialog { + /** + * Waiting for the dialog to appear. + */ + async waitForDialog() { + const input = this.participant.driver.$(INPUT_KEY_XPATH); + + await input.waitForExist({ + timeout: 5000, + timeoutMsg: 'Password dialog not found' + }); + await input.waitForDisplayed(); + } + + /** + * Sets a password and submits the dialog. + * @param password + */ + async submitPassword(password: string) { + const passwordInput = this.participant.driver.$(INPUT_KEY_XPATH); + + await passwordInput.waitForExist(); + await passwordInput.click(); + await passwordInput.clearValue(); + + await this.participant.driver.keys(password); + + await this.clickOkButton(); + } +} diff --git a/tests/pageobjects/SecurityDialog.ts b/tests/pageobjects/SecurityDialog.ts index e705f7a9e9..8a348b82c4 100644 --- a/tests/pageobjects/SecurityDialog.ts +++ b/tests/pageobjects/SecurityDialog.ts @@ -7,6 +7,7 @@ const ADD_PASSWORD_FIELD = 'info-password-input'; const DIALOG_CONTAINER = 'security-dialog'; const LOCAL_LOCK = 'info-password-local'; const REMOTE_LOCK = 'info-password-remote'; +const REMOVE_PASSWORD = 'remove-password'; /** * Page object for the security dialog. @@ -133,4 +134,18 @@ export default class SecurityDialog extends BaseDialog { expect(validationMessage).toBe(''); } } + + /** + * Removes the password from the current conference through the security dialog, if a password is set. + */ + async removePassword() { + if (!await this.isLocked()) { + return; + } + + const removePassword = this.participant.driver.$(`.${REMOVE_PASSWORD}`); + + await removePassword.waitForClickable(); + await removePassword.click(); + } } diff --git a/tests/specs/2way/lockRoom.spec.ts b/tests/specs/2way/lockRoom.spec.ts new file mode 100644 index 0000000000..20ff4ec402 --- /dev/null +++ b/tests/specs/2way/lockRoom.spec.ts @@ -0,0 +1,171 @@ +import { ensureOneParticipant, ensureTwoParticipants, joinSecondParticipant } from '../../helpers/participants'; + +/** + * 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(ctx)); + + it('locks the room', () => participant1LockRoom()); + + it('enter participant in locked room', async () => { + // first enter wrong pin then correct one + await joinSecondParticipant(ctx, { + skipWaitToJoin: true, + skipInMeetingChecks: true + }); + + const { p2 } = ctx; + + // wait for password prompt + const p2PasswordDialog = p2.getPasswordDialog(); + + await p2PasswordDialog.waitForDialog(); + await p2PasswordDialog.submitPassword(`${ctx.roomKey}1234`); + + // wait for password prompt + await p2PasswordDialog.waitForDialog(); + await p2PasswordDialog.submitPassword(ctx.roomKey); + + await p2.waitToJoinMUC(); + + const p2SecurityDialog = p2.getSecurityDialog(); + + await p2.getToolbar().clickSecurityButton(); + await p2SecurityDialog.waitForDisplay(); + + expect(await p2SecurityDialog.isLocked()).toBe(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(ctx); + + const { p2 } = ctx; + const p2SecurityDialog = p2.getSecurityDialog(); + + await p2.getToolbar().clickSecurityButton(); + await p2SecurityDialog.waitForDisplay(); + + expect(await p2SecurityDialog.isLocked()).toBe(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(); + + expect(await p2SecurityDialog.isLocked()).toBe(true); + + await participant1UnlockRoom(); + + expect(await p2SecurityDialog.isLocked()).toBe(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(ctx, { + skipWaitToJoin: true, + skipInMeetingChecks: true + }); + + const { p2 } = ctx; + + // wait for password prompt + const p2PasswordDialog = p2.getPasswordDialog(); + + await p2PasswordDialog.waitForDialog(); + await p2PasswordDialog.submitPassword(`${ctx.roomKey}1234`); + + // 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(); + + expect(await p2SecurityDialog.isLocked()).toBe(false); + }); +}); + +/** + * Participant1 locks the room. + */ +async function participant1LockRoom() { + ctx.roomKey = `${Math.trunc(Math.random() * 1_000_000)}`; + + const { p1 } = ctx; + const p1SecurityDialog = p1.getSecurityDialog(); + + await p1.getToolbar().clickSecurityButton(); + await p1SecurityDialog.waitForDisplay(); + + expect(await p1SecurityDialog.isLocked()).toBe(false); + + await p1SecurityDialog.addPassword(ctx.roomKey); + + await p1SecurityDialog.clickCloseButton(); + + await p1.getToolbar().clickSecurityButton(); + await p1SecurityDialog.waitForDisplay(); + + expect(await p1SecurityDialog.isLocked()).toBe(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 p1.driver.waitUntil( + async () => !await p1SecurityDialog.isLocked(), + { + timeout: 3_000, // 3 seconds + timeoutMsg: `Timeout waiting for the room to unlock for ${p1.name}.` + } + ); + + await p1SecurityDialog.clickCloseButton(); +}