diff --git a/tests/helpers/Participant.ts b/tests/helpers/Participant.ts index 314f66f97a..eea8e0603a 100644 --- a/tests/helpers/Participant.ts +++ b/tests/helpers/Participant.ts @@ -342,6 +342,21 @@ export class Participant { }); } + /** + * Waits for ICE to get connected on the p2p connection. + * + * @returns {Promise} + */ + async waitForP2PIceConnected(): Promise { + const driver = this.driver; + + return driver.waitUntil(() => + driver.execute(() => APP?.conference?.getP2PConnectionState() === 'connected'), { + timeout: 15_000, + timeoutMsg: `expected P2P ICE to be connected for 15s for ${this.name}` + }); + } + /** * Waits for send and receive data. * diff --git a/tests/helpers/participants.ts b/tests/helpers/participants.ts index 428c4c77e5..388d982905 100644 --- a/tests/helpers/participants.ts +++ b/tests/helpers/participants.ts @@ -334,3 +334,14 @@ export async function checkSubject(participant: Participant, subject: string) { expect(txt.startsWith(subject)).toBe(true); } + +/** + * Check if a screensharing tile is displayed on the observer. + * Expects there was already a video by this participant and screen sharing will be the second video `-v1`. + */ +export async function checkForScreensharingTile(sharer: Participant, observer: Participant, reverse = false) { + await observer.driver.$(`//span[@id='participant_${await sharer.getEndpointId()}-v1']`).waitForDisplayed({ + timeout: 3_000, + reverse + }); +} diff --git a/tests/pageobjects/Filmstrip.ts b/tests/pageobjects/Filmstrip.ts index f3a733022a..49bbd35e20 100644 --- a/tests/pageobjects/Filmstrip.ts +++ b/tests/pageobjects/Filmstrip.ts @@ -136,11 +136,7 @@ export default class Filmstrip extends BasePageObject { * @param participant */ async muteAudio(participant: Participant) { - const participantId = await participant.getEndpointId(); - - await this.participant.driver.$(`#participant-item-${participantId}`).moveTo(); - - await this.participant.driver.$(`button[data-testid="mute-audio-${participantId}"]`).click(); + await this.clickOnRemoteMenuLink(await participant.getEndpointId(), 'mutelink', false); } /** diff --git a/tests/specs/2way/mute.spect.ts b/tests/specs/2way/mute.spect.ts new file mode 100644 index 0000000000..0c120bf9dd --- /dev/null +++ b/tests/specs/2way/mute.spect.ts @@ -0,0 +1,142 @@ +import type { Participant } from '../../helpers/Participant'; +import { + checkForScreensharingTile, + ensureOneParticipant, + ensureTwoParticipants, + joinSecondParticipant +} from '../../helpers/participants'; + +describe('Mute', () => { + it('joining the meeting', () => ensureTwoParticipants(ctx)); + + 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 p2.getToolbar().clickAudioUnmuteButton(); + + // and now check whether second participant is muted + await p1.getFilmstrip().assertAudioMuteIconIsDisplayed(p2, true); + }); + + it('p1 mutes before p2 joins', async () => { + await ctx.p2.hangup(); + + const { p1 } = ctx; + + await p1.getToolbar().clickAudioMuteButton(); + + await ensureTwoParticipants(ctx); + + 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 testee.getToolbar().clickAudioMuteButton(); + } else { + await testee.getToolbar().clickAudioUnmuteButton(); + } + + await observer.getFilmstrip().assertAudioMuteIconIsDisplayed(testee, !muted); + await testee.getFilmstrip().assertAudioMuteIconIsDisplayed(testee, !muted); +} + +/** + * 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(ctx, { + configOverwrite: { + p2p: { + enabled: p2p + } + } + }); + + const { p1 } = ctx; + + await p1.getToolbar().clickVideoMuteButton(); + + await joinSecondParticipant(ctx, { + configOverwrite: { + p2p: { + enabled: p2p + } + } + }); + + const { p2 } = ctx; + + if (p2p) { + await p2.waitForP2PIceConnected(); + } else { + await p2.waitForIceConnected(); + } + + await p2.waitForSendReceiveData({ checkReceive: false }); + + // 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 p1.getToolbar().clickVideoUnmuteButton(); + await p2.getParticipantsPane().assertVideoMuteIconIsDisplayed(p1, true); + await p2.waitForRemoteVideo(await p1.getEndpointId()); +} diff --git a/tests/specs/4way/desktopSharing.spec.ts b/tests/specs/4way/desktopSharing.spec.ts index eb6a519ad1..b6358c5e39 100644 --- a/tests/specs/4way/desktopSharing.spec.ts +++ b/tests/specs/4way/desktopSharing.spec.ts @@ -1,6 +1,6 @@ import { SET_AUDIO_ONLY } from '../../../react/features/base/audio-only/actionTypes'; -import type { Participant } from '../../helpers/Participant'; import { + checkForScreensharingTile, ensureFourParticipants, ensureOneParticipant, ensureThreeParticipants, @@ -311,12 +311,3 @@ describe('Desktop sharing', () => { }); }); -/** - * Check if a screensharing tile is displayed on the observer. - */ -async function checkForScreensharingTile(sharer: Participant, observer: Participant, reverse = false) { - await observer.driver.$(`//span[@id='participant_${await sharer.getEndpointId()}-v1']`).waitForDisplayed({ - timeout: 3_000, - reverse - }); -}