diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx
index 7e5a16db04..7e2b61aa0d 100644
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-controls/input-stream-live-selector/component.jsx
@@ -245,9 +245,10 @@ class InputStreamLiveSelector extends Component {
];
const deviceList = (listLength > 0)
- ? list.map((device) => (
+ ? list.map((device, index) => (
{
key: `${device.deviceId}-${deviceKind}`,
+ dataTest: `${deviceKind}-${index + 1}`,
label: InputStreamLiveSelector.truncateDeviceName(device.label),
customStyles: (device.deviceId === currentDeviceId) && Styled.SelectedLabel,
iconRight: (device.deviceId === currentDeviceId) ? 'check' : null,
@@ -316,7 +317,7 @@ class InputStreamLiveSelector extends Component {
aria-label={intl.formatMessage(intlMessages.leaveAudio)}
label={intl.formatMessage(intlMessages.leaveAudio)}
accessKey={shortcuts.leaveaudio}
- data-test='leaveListenOnly'
+ data-test="leaveListenOnly"
hideLabel
color="primary"
icon={isListenOnly ? 'listen' : 'volume_level_2'}
@@ -397,7 +398,7 @@ class InputStreamLiveSelector extends Component {
? this.renderListenOnlyButton()
: this.renderMuteToggleButton()}
0 ? 'hasVolume' : 'hasNoVolume'}
min={volumeFloor}
low={low}
max={high * 1.25}
diff --git a/bigbluebutton-tests/playwright/audio/audio.js b/bigbluebutton-tests/playwright/audio/audio.js
index 6c45ff6325..89197e5984 100644
--- a/bigbluebutton-tests/playwright/audio/audio.js
+++ b/bigbluebutton-tests/playwright/audio/audio.js
@@ -1,6 +1,7 @@
const Page = require('../core/page');
const e = require('../core/elements');
-const { ELEMENT_WAIT_LONGER_TIME, ELEMENT_WAIT_TIME } = require('../core/constants');
+const { ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
+const { connectMicrophone, isAudioItemSelected } = require('./util');
class Audio extends Page {
constructor(browser, page) {
@@ -18,26 +19,58 @@ class Audio extends Page {
}
async joinMicrophone() {
- const {
- autoJoinAudioModal,
- skipEchoTest,
- skipEchoTestOnJoin,
- } = this.settings;
-
- if (!autoJoinAudioModal) await this.waitAndClick(e.joinAudio);
- await this.waitAndClick(e.microphoneButton);
- const shouldSkipEchoTest = skipEchoTest || skipEchoTestOnJoin;
- if (!shouldSkipEchoTest) {
- await this.waitForSelector(e.stopHearingButton);
- await this.waitAndClick(e.joinEchoTestButton);
- await this.waitForSelector(e.establishingAudioLabel);
- await this.wasRemoved(e.establishingAudioLabel, ELEMENT_WAIT_LONGER_TIME);
- }
- await this.hasElement(e.isTalking);
+ await connectMicrophone(this);
await this.hasElement(e.muteMicButton);
await this.waitAndClick(e.audioDropdownMenu);
await this.hasElement(e.leaveAudio);
}
+
+ async muteYourselfByButton() {
+ await connectMicrophone(this);
+ await this.waitAndClick(e.muteMicButton);
+ await this.wasRemoved(e.isTalking);
+ await this.hasElement(e.wasTalking);
+ await this.wasRemoved(e.muteMicButton);
+ await this.hasElement(e.unmuteMicButton);
+ await this.wasRemoved(e.talkingIndicator, ELEMENT_WAIT_LONGER_TIME);
+ }
+
+ async changeAudioInput() {
+ await connectMicrophone(this);
+ await this.waitAndClick(e.audioDropdownMenu);
+ await isAudioItemSelected(this, e.defaultInputAudioDevice);
+ await this.waitAndClick(e.secondInputAudioDevice);
+ await this.hasElement(e.isTalking);
+ await this.hasElement(e.muteMicButton);
+ await this.waitAndClick(e.audioDropdownMenu);
+ await isAudioItemSelected(this, e.secondInputAudioDevice);
+ }
+
+ async keepMuteStateOnRejoin() {
+ await connectMicrophone(this);
+ await this.waitAndClick(e.muteMicButton);
+ await this.hasElement(e.wasTalking);
+ await this.wasRemoved(e.muteMicButton);
+ await this.hasElement(e.unmuteMicButton);
+ await this.waitAndClick(e.audioDropdownMenu);
+ await this.waitAndClick(e.leaveAudio);
+ await this.waitAndClick(e.joinAudio);
+ await this.waitAndClick(e.microphoneButton);
+ await this.waitAndClick(e.joinEchoTestButton);
+ await this.waitForSelector(e.establishingAudioLabel);
+ await this.wasRemoved(e.establishingAudioLabel, ELEMENT_WAIT_LONGER_TIME);
+ await this.hasElement(e.unmuteMicButton);
+ }
+
+ async muteYourselfBytalkingIndicator() {
+ await connectMicrophone(this);
+ await this.waitAndClick(e.talkingIndicator);
+ await this.hasElement(e.wasTalking);
+ await this.wasRemoved(e.muteMicButton);
+ await this.hasElement(e.unmuteMicButton);
+ await this.wasRemoved(e.isTalking);
+ await this.wasRemoved(e.talkingIndicator, ELEMENT_WAIT_LONGER_TIME);
+ }
}
exports.Audio = Audio;
diff --git a/bigbluebutton-tests/playwright/audio/audio.spec.js b/bigbluebutton-tests/playwright/audio/audio.spec.js
index d1b08dfee6..392a811140 100644
--- a/bigbluebutton-tests/playwright/audio/audio.spec.js
+++ b/bigbluebutton-tests/playwright/audio/audio.spec.js
@@ -1,4 +1,5 @@
const { test } = require('@playwright/test');
+const { MultiUsers } = require('../user/multiusers');
const { Audio } = require('./audio');
test.describe.parallel('Audio', () => {
@@ -13,4 +14,37 @@ test.describe.parallel('Audio', () => {
await audio.init(true, false);
await audio.joinMicrophone();
});
+
+ test('Mute youself by clicking the mute button @ci', async ({ browser, page }) => {
+ const audio = new Audio(browser, page);
+ await audio.init(true, false);
+ await audio.muteYourselfByButton();
+ });
+
+ test('Change audio input and keep it connected', async ({ browser, page }) => {
+ const audio = new Audio(browser, page);
+ await audio.init(true, false);
+ await audio.changeAudioInput();
+ });
+
+ test('Keep the last mute state after rejoining audio @ci', async ({ browser, page }) => {
+ const audio = new Audio(browser, page);
+ await audio.init(true, false);
+ await audio.keepMuteStateOnRejoin();
+ });
+
+ test.describe.parallel('Talking indicator @ci', () => {
+ test('Mute youself by clicking the talking indicator', async ({ browser, page }) => {
+ const audio = new Audio(browser, page);
+ await audio.init(true, false);
+ await audio.muteYourselfBytalkingIndicator();
+ });
+
+ test('Mute another user by clicking the talking indicator', async ({ browser, context, page }) => {
+ const audio = new MultiUsers(browser, context);
+ await audio.initModPage(page);
+ await audio.initUserPage(false);
+ await audio.muteAnotherUser();
+ });
+ });
});
diff --git a/bigbluebutton-tests/playwright/audio/util.js b/bigbluebutton-tests/playwright/audio/util.js
new file mode 100644
index 0000000000..3ced63d5f9
--- /dev/null
+++ b/bigbluebutton-tests/playwright/audio/util.js
@@ -0,0 +1,33 @@
+const { expect } = require('@playwright/test');
+const e = require('../core/elements');
+const { ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
+
+async function connectMicrophone(testPage) {
+ const {
+ autoJoinAudioModal,
+ skipEchoTest,
+ skipEchoTestOnJoin,
+ } = testPage.settings;
+
+ if (!autoJoinAudioModal) await testPage.waitAndClick(e.joinAudio);
+ await testPage.waitAndClick(e.microphoneButton);
+ const shouldSkipEchoTest = skipEchoTest || skipEchoTestOnJoin;
+ if (!shouldSkipEchoTest) {
+ await testPage.waitForSelector(e.stopHearingButton);
+ await testPage.waitAndClick(e.joinEchoTestButton);
+ await testPage.waitForSelector(e.establishingAudioLabel);
+ await testPage.wasRemoved(e.establishingAudioLabel, ELEMENT_WAIT_LONGER_TIME);
+ }
+ await testPage.hasElement(e.isTalking);
+}
+
+async function isAudioItemSelected(testPage, audioSelector) {
+ await testPage.waitForSelector(audioSelector);
+ const isSelected = await testPage.page.evaluate(([selector, icon]) => {
+ return !!document.querySelector(selector).firstChild.querySelector(icon);
+ }, [audioSelector, e.checkedIcon]);
+ await expect(isSelected).toBeTruthy();
+}
+
+exports.connectMicrophone = connectMicrophone;
+exports.isAudioItemSelected = isAudioItemSelected;
diff --git a/bigbluebutton-tests/playwright/core/elements.js b/bigbluebutton-tests/playwright/core/elements.js
index 6a9676060f..68b4327d17 100644
--- a/bigbluebutton-tests/playwright/core/elements.js
+++ b/bigbluebutton-tests/playwright/core/elements.js
@@ -33,10 +33,15 @@ exports.establishingAudioLabel = 'span[data-test="establishingAudioLabel"]';
exports.leaveListenOnly = 'button[data-test="leaveListenOnly"]';
exports.leaveAudio = 'li[data-test="leaveAudio"]';
exports.audioDropdownMenu = 'button[data-test="audioDropdownMenu"]';
+exports.defaultInputAudioDevice = 'li[data-test="audioinput-1"]';
+exports.secondInputAudioDevice = 'li[data-test="audioinput-2"]';
exports.microphoneButton = 'button[data-test="microphoneBtn"]';
exports.echoYesButton = 'button[data-test="echoYesBtn"]';
exports.connectingToEchoTest = 'span[data-test="connectingToEchoTest"]';
+exports.hasVolumeEchoTest = 'span[data-test="hasVolume"]';
+exports.hasNoVolumeEchoTest = 'span[data-test="hasNoVolume"]';
exports.isTalking = 'button[data-test="isTalking"]';
+exports.wasTalking = 'button[data-test="wasTalking"]';
exports.talkingIndicator = 'div[data-test="talkingIndicator"]';
exports.unmuteMicButton = 'button[data-test="unmuteMicButton"]';
exports.muteMicButton = 'button[data-test="muteMicButton"]';
@@ -125,6 +130,7 @@ exports.loweringHandToast = 'Your hand has been lowered';
const baseBbbIcon = 'i.icon-bbb-';
exports.unmuteIcon = `${baseBbbIcon}unmute`;
exports.listenOnlyIcon = `${baseBbbIcon}listen`;
+exports.checkedIcon = `${baseBbbIcon}check`;
// Polling
exports.pollQuestion = 'Are we good ?';
diff --git a/bigbluebutton-tests/playwright/user/multiusers.js b/bigbluebutton-tests/playwright/user/multiusers.js
index 5e69796a18..2cc7925056 100644
--- a/bigbluebutton-tests/playwright/user/multiusers.js
+++ b/bigbluebutton-tests/playwright/user/multiusers.js
@@ -6,6 +6,7 @@ const { sleep } = require('../core/helpers');
const { checkAvatarIcon, checkIsPresenter } = require('./util');
const { checkTextContent } = require('../core/util');
const { getSettings } = require('../core/settings');
+const { ELEMENT_WAIT_LONGER_TIME } = require('../core/constants');
class MultiUsers {
constructor(browser, context) {
@@ -64,6 +65,17 @@ class MultiUsers {
await this.userPage2.init(false, shouldCloseAudioModal, options);
}
+ async muteAnotherUser() {
+ await this.userPage.joinMicrophone();
+ await this.modPage.hasElement(e.joinAudio);
+ await this.modPage.waitAndClick(e.isTalking);
+ await this.userPage.hasElement(e.unmuteMicButton);
+ await this.modPage.hasElement(e.wasTalking);
+ await this.userPage.hasElement(e.wasTalking);
+ await this.userPage.wasRemoved(e.talkingIndicator, ELEMENT_WAIT_LONGER_TIME);
+ await this.modPage.wasRemoved(e.talkingIndicator);
+ }
+
async userPresence() {
const firstUserOnModPage = this.modPage.getLocator(e.currentUser);
const secondUserOnModPage = this.modPage.getLocator(e.userListItem);