From 6def45b59c7cfa790eafeec3ad8da3abe9486a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Sun, 5 Jun 2022 11:21:53 +0200 Subject: [PATCH 1/8] settings: reorganize VoiseUserSettingsTab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a preparation step for adding new advanced voice processing options to the settings tab. 3 sections have been added: Voice, Video, and Advanced Signed-off-by: László Várady --- .../tabs/user/VoiceUserSettingsTab.tsx | 33 ++++++++++++------- src/i18n/strings/en_EN.json | 3 ++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx index dbd22deb4bd..e5ff0fbf465 100644 --- a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx @@ -183,22 +183,33 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> { return (
{ _t("Voice & Video") }
+ { requestButton }
- { requestButton } + { _t("Voice settings") } { speakerDropdown } { microphoneDropdown } +
+
+ { _t("Video settings") } { webcamDropdown } - - +
+ +
{ _t("Advanced") }
+
+
+ { _t("Connection") } + + +
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f40e1658040..4f1a45a455d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1618,6 +1618,9 @@ "No Microphones detected": "No Microphones detected", "Camera": "Camera", "No Webcams detected": "No Webcams detected", + "Voice settings": "Voice settings", + "Video settings": "Video settings", + "Connection": "Connection", "This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers", "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.", "Upgrade this space to the recommended room version": "Upgrade this space to the recommended room version", From 538ad86eea8b91a17b71c314049ecc3dce27a8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Sun, 5 Jun 2022 12:05:43 +0200 Subject: [PATCH 2/8] Add advanced audio settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit autoGainControl, echoCancellation, and noiseSuppression are audio processing options that are usually enabled by default on WebRTC input tracks. This commit adds the possibility to enable/disable them, as they can be undesirable in some cases (audiophile use cases). For example, one might want to stream electronic dance music, which is basically noise, so it should not be suppressed in that specific case. Signed-off-by: László Várady --- src/MediaDeviceHandler.ts | 37 +++++++++++++++++++ .../tabs/user/VoiceUserSettingsTab.tsx | 34 +++++++++++++++++ src/i18n/strings/en_EN.json | 4 ++ src/settings/Settings.tsx | 15 ++++++++ 4 files changed, 90 insertions(+) diff --git a/src/MediaDeviceHandler.ts b/src/MediaDeviceHandler.ts index 0e6d2b98bc7..6d60bc72f0d 100644 --- a/src/MediaDeviceHandler.ts +++ b/src/MediaDeviceHandler.ts @@ -88,6 +88,16 @@ export default class MediaDeviceHandler extends EventEmitter { await MatrixClientPeg.get().getMediaHandler().setAudioInput(audioDeviceId); await MatrixClientPeg.get().getMediaHandler().setVideoInput(videoDeviceId); + + await MediaDeviceHandler.updateAudioSettings(); + } + + private static async updateAudioSettings(): Promise { + await MatrixClientPeg.get().getMediaHandler().setAudioSettings({ + autoGainControl: MediaDeviceHandler.getAudioAutoGainControl(), + echoCancellation: MediaDeviceHandler.getAudioEchoCancellation(), + noiseSuppression: MediaDeviceHandler.getAudioNoiseSuppression(), + }); } public setAudioOutput(deviceId: string): void { @@ -123,6 +133,21 @@ export default class MediaDeviceHandler extends EventEmitter { } } + public static async setAudioAutoGainControl(value: boolean): Promise { + await SettingsStore.setValue("webrtc_audio_autoGainControl", null, SettingLevel.DEVICE, value); + await MediaDeviceHandler.updateAudioSettings(); + } + + public static async setAudioEchoCancellation(value: boolean): Promise { + await SettingsStore.setValue("webrtc_audio_echoCancellation", null, SettingLevel.DEVICE, value); + await MediaDeviceHandler.updateAudioSettings(); + } + + public static async setAudioNoiseSuppression(value: boolean): Promise { + await SettingsStore.setValue("webrtc_audio_noiseSuppression", null, SettingLevel.DEVICE, value); + await MediaDeviceHandler.updateAudioSettings(); + } + public static getAudioOutput(): string { return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_audiooutput"); } @@ -135,6 +160,18 @@ export default class MediaDeviceHandler extends EventEmitter { return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_videoinput"); } + public static getAudioAutoGainControl(): boolean { + return SettingsStore.getValue("webrtc_audio_autoGainControl"); + } + + public static getAudioEchoCancellation(): boolean { + return SettingsStore.getValue("webrtc_audio_echoCancellation"); + } + + public static getAudioNoiseSuppression(): boolean { + return SettingsStore.getValue("webrtc_audio_noiseSuppression"); + } + /** * Returns the current set deviceId for a device kind * @param {MediaDeviceKindEnum} kind of the device that will be returned diff --git a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx index e5ff0fbf465..2f61112ddf3 100644 --- a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx @@ -27,6 +27,7 @@ import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; import Modal from "../../../../../Modal"; import { SettingLevel } from "../../../../../settings/SettingLevel"; import SettingsFlag from '../../../elements/SettingsFlag'; +import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import ErrorDialog from '../../../dialogs/ErrorDialog'; const getDefaultDevice = (devices: Array>) => { @@ -43,6 +44,9 @@ const getDefaultDevice = (devices: Array>) => { interface IState extends Record { mediaDevices: IMediaDevices; + audioAutoGainControl: boolean; + audioEchoCancellation: boolean; + audioNoiseSuppression: boolean; } export default class VoiceUserSettingsTab extends React.Component<{}, IState> { @@ -54,6 +58,9 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> { [MediaDeviceKindEnum.AudioOutput]: null, [MediaDeviceKindEnum.AudioInput]: null, [MediaDeviceKindEnum.VideoInput]: null, + audioAutoGainControl: MediaDeviceHandler.getAudioAutoGainControl(), + audioEchoCancellation: MediaDeviceHandler.getAudioEchoCancellation(), + audioNoiseSuppression: MediaDeviceHandler.getAudioNoiseSuppression(), }; } @@ -197,6 +204,33 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> {
{ _t("Advanced") }
+ { _t("Voice processing") } +
+ { + await MediaDeviceHandler.setAudioAutoGainControl(v); + this.setState({ audioAutoGainControl: MediaDeviceHandler.getAudioAutoGainControl() }); + }} + label={_t("Automatic gain control")} + /> + { + await MediaDeviceHandler.setAudioEchoCancellation(v); + this.setState({ audioEchoCancellation: MediaDeviceHandler.getAudioEchoCancellation() }); + }} + label={_t("Echo cancellation")} + /> + { + await MediaDeviceHandler.setAudioNoiseSuppression(v); + this.setState({ audioNoiseSuppression: MediaDeviceHandler.getAudioNoiseSuppression() }); + }} + label={_t("Noise suppression")} + /> +
{ _t("Connection") } Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "Warning: Upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 723b789ab01..d4b602bd402 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -731,6 +731,21 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, default: "default", }, + "webrtc_audio_autoGainControl": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("Automatic gain control"), + default: true, + }, + "webrtc_audio_echoCancellation": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("Echo cancellation"), + default: true, + }, + "webrtc_audio_noiseSuppression": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("Noise suppression"), + default: true, + }, "language": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, default: "en", From 080b91dc698a7466896217b65aa107e436917c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Wed, 8 Jun 2022 20:13:33 +0200 Subject: [PATCH 3/8] settings: use composition for VoiceUserSettingsTab's state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IState does not seem to be in an "IS-A" relation with MediaDeviceKindEnum-based records (Liskov substitution principle), so applying the "composition over inheritance" concept might be a good idea. This can be refactored further by adding separate types for I/O device options and for audio settings and then using them in IState via composition. Signed-off-by: László Várady --- .../views/settings/tabs/user/VoiceUserSettingsTab.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx index 2f61112ddf3..6190cba9b6e 100644 --- a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx @@ -42,8 +42,11 @@ const getDefaultDevice = (devices: Array>) => { } }; -interface IState extends Record { +interface IState { mediaDevices: IMediaDevices; + [MediaDeviceKindEnum.AudioOutput]: string; + [MediaDeviceKindEnum.AudioInput]: string; + [MediaDeviceKindEnum.VideoInput]: string; audioAutoGainControl: boolean; audioEchoCancellation: boolean; audioNoiseSuppression: boolean; From aee444961cc3b69a7186b70181e55a797acbbdd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Sun, 21 Aug 2022 22:35:23 +0200 Subject: [PATCH 4/8] settings: add secondary line descriptions for p2p calls and fallbackICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: László Várady --- src/i18n/strings/en_EN.json | 6 ++++-- src/settings/Settings.tsx | 13 ++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 12c8dc2f915..154dd3194c5 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -976,7 +976,8 @@ "Match system theme": "Match system theme", "Use a system font": "Use a system font", "System font name": "System font name", - "Allow Peer-to-Peer for 1:1 calls (if you enable this, the other party might be able to see your IP address)": "Allow Peer-to-Peer for 1:1 calls (if you enable this, the other party might be able to see your IP address)", + "Allow Peer-to-Peer for 1:1 calls": "Allow Peer-to-Peer for 1:1 calls", + "When enabled, the other party might be able to see your IP address": "When enabled, the other party might be able to see your IP address", "Automatic gain control": "Automatic gain control", "Echo cancellation": "Echo cancellation", "Noise suppression": "Noise suppression", @@ -995,7 +996,8 @@ "Show shortcut to welcome checklist above the room list": "Show shortcut to welcome checklist above the room list", "Show hidden events in timeline": "Show hidden events in timeline", "Low bandwidth mode (requires compatible homeserver)": "Low bandwidth mode (requires compatible homeserver)", - "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)", + "Allow fallback call assist server (turn.matrix.org)": "Allow fallback call assist server (turn.matrix.org)", + "Only applies if your homeserver does not offer one. Your IP address would be shared during a call.": "Only applies if your homeserver does not offer one. Your IP address would be shared during a call.", "Show previews/thumbnails for images": "Show previews/thumbnails for images", "Enable message search in encrypted rooms": "Enable message search in encrypted rooms", "How fast should messages be downloaded.": "How fast should messages be downloaded.", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index d4b602bd402..076999a69de 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -712,10 +712,8 @@ export const SETTINGS: {[setting: string]: ISetting} = { }, "webRtcAllowPeerToPeer": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, - displayName: _td( - "Allow Peer-to-Peer for 1:1 calls " + - "(if you enable this, the other party might be able to see your IP address)", - ), + displayName: _td("Allow Peer-to-Peer for 1:1 calls"), + description: _td("When enabled, the other party might be able to see your IP address"), default: true, invertedSettingName: 'webRtcForceTURN', }, @@ -917,9 +915,10 @@ export const SETTINGS: {[setting: string]: ISetting} = { }, "fallbackICEServerAllowed": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, - displayName: _td( - "Allow fallback call assist server turn.matrix.org when your homeserver " + - "does not offer one (your IP address would be shared during a call)", + displayName: _td("Allow fallback call assist server (turn.matrix.org)"), + description: _td( + "Only applies if your homeserver does not offer one. " + + "Your IP address would be shared during a call.", ), // This is a tri-state value, where `null` means "prompt the user". default: null, From 328c2f31e416646facbaf871ed38f5fd7fbc7376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Sat, 22 Oct 2022 18:46:51 +0200 Subject: [PATCH 5/8] settings: UX refactor of advanced audio processing options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: László Várady --- .../tabs/user/VoiceUserSettingsTab.tsx | 24 +++++++++---------- src/i18n/strings/en_EN.json | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx index 6190cba9b6e..fcbf1995bd3 100644 --- a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx @@ -198,6 +198,14 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> { { _t("Voice settings") } { speakerDropdown } { microphoneDropdown } + { + await MediaDeviceHandler.setAudioAutoGainControl(v); + this.setState({ audioAutoGainControl: MediaDeviceHandler.getAudioAutoGainControl() }); + }} + label={_t("Automatically adjust the microphone volume")} + />
{ _t("Video settings") } @@ -210,12 +218,12 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> { { _t("Voice processing") }
{ - await MediaDeviceHandler.setAudioAutoGainControl(v); - this.setState({ audioAutoGainControl: MediaDeviceHandler.getAudioAutoGainControl() }); + await MediaDeviceHandler.setAudioNoiseSuppression(v); + this.setState({ audioNoiseSuppression: MediaDeviceHandler.getAudioNoiseSuppression() }); }} - label={_t("Automatic gain control")} + label={_t("Noise suppression")} /> { }} label={_t("Echo cancellation")} /> - { - await MediaDeviceHandler.setAudioNoiseSuppression(v); - this.setState({ audioNoiseSuppression: MediaDeviceHandler.getAudioNoiseSuppression() }); - }} - label={_t("Noise suppression")} - />
{ _t("Connection") } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 154dd3194c5..5aee719a203 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1624,6 +1624,7 @@ "Camera": "Camera", "No Webcams detected": "No Webcams detected", "Voice settings": "Voice settings", + "Automatically adjust the microphone volume": "Automatically adjust the microphone volume", "Video settings": "Video settings", "Voice processing": "Voice processing", "Connection": "Connection", From 4187efd08987a42ee8ab04a413890c85b88bcfae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Tue, 8 Nov 2022 21:44:02 +0100 Subject: [PATCH 6/8] test: add test for MediaDeviceHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: László Várady --- test/MediaDeviceHandler-test.ts | 65 +++++++++++++++++++++++++++++++++ test/test-utils/test-utils.ts | 1 + 2 files changed, 66 insertions(+) create mode 100644 test/MediaDeviceHandler-test.ts diff --git a/test/MediaDeviceHandler-test.ts b/test/MediaDeviceHandler-test.ts new file mode 100644 index 00000000000..359ba5fc53d --- /dev/null +++ b/test/MediaDeviceHandler-test.ts @@ -0,0 +1,65 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { mocked } from 'jest-mock'; + +import { SettingLevel } from "../src/settings/SettingLevel"; +import { MatrixClientPeg } from '../src/MatrixClientPeg'; +import { stubClient } from "./test-utils"; +import MediaDeviceHandler from "../src/MediaDeviceHandler"; +import SettingsStore from '../src/settings/SettingsStore'; + +jest.mock("../src/settings/SettingsStore"); + +const SettingsStoreMock = mocked(SettingsStore); + +describe("MediaDeviceHandler", () => { + beforeEach(() => { + stubClient(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it("sets audio settings", async () => { + const expectedAudioSettings = new Map([ + ["webrtc_audio_autoGainControl", false], + ["webrtc_audio_echoCancellation", true], + ["webrtc_audio_noiseSuppression", false], + ]); + + SettingsStoreMock.getValue.mockImplementation((settingName): any => { + return expectedAudioSettings.get(settingName); + }); + + await MediaDeviceHandler.setAudioAutoGainControl(false); + await MediaDeviceHandler.setAudioEchoCancellation(true); + await MediaDeviceHandler.setAudioNoiseSuppression(false); + + expectedAudioSettings.forEach((value, key) => { + expect(SettingsStoreMock.setValue).toHaveBeenCalledWith( + key, null, SettingLevel.DEVICE, value, + ); + }); + + expect(MatrixClientPeg.get().getMediaHandler().setAudioSettings).toHaveBeenCalledWith({ + autoGainControl: false, + echoCancellation: true, + noiseSuppression: false, + }); + }); +}); diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 1a8792b810a..ef95d6d5a76 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -185,6 +185,7 @@ export function createTestClient(): MatrixClient { getMediaHandler: jest.fn().mockReturnValue({ setVideoInput: jest.fn(), setAudioInput: jest.fn(), + setAudioSettings: jest.fn(), } as unknown as MediaHandler), uploadContent: jest.fn(), getEventMapper: () => (opts) => new MatrixEvent(opts), From bf024e2e0867a2e37b27f6e37bd317f89e9cfa09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Wed, 9 Nov 2022 19:11:53 +0100 Subject: [PATCH 7/8] settings: allow null values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are multiple examples of options with null defaults. Signed-off-by: László Várady --- src/settings/Settings.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 076999a69de..81856cc9f3b 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -127,7 +127,8 @@ export type SettingValueType = boolean | string | number[] | string[] | - Record; + Record | + null; export interface IBaseSetting { isFeature?: false | undefined; From 69b1eec898859de7a7aa9cd023e2e54d6f585204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Wed, 9 Nov 2022 20:34:59 +0100 Subject: [PATCH 8/8] test: add test for VoiceUserSettingsTab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: László Várady --- .../tabs/user/VoiceUserSettingsTab.tsx | 3 + .../tabs/user/VoiceUserSettingsTab-test.tsx | 56 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 test/components/views/settings/tabs/user/VoiceUserSettingsTab-test.tsx diff --git a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx index fcbf1995bd3..f447158ccc1 100644 --- a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.tsx @@ -205,6 +205,7 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> { this.setState({ audioAutoGainControl: MediaDeviceHandler.getAudioAutoGainControl() }); }} label={_t("Automatically adjust the microphone volume")} + data-testid='voice-auto-gain' />
@@ -224,6 +225,7 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> { this.setState({ audioNoiseSuppression: MediaDeviceHandler.getAudioNoiseSuppression() }); }} label={_t("Noise suppression")} + data-testid='voice-noise-suppression' /> { this.setState({ audioEchoCancellation: MediaDeviceHandler.getAudioEchoCancellation() }); }} label={_t("Echo cancellation")} + data-testid='voice-echo-cancellation' />
diff --git a/test/components/views/settings/tabs/user/VoiceUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/VoiceUserSettingsTab-test.tsx new file mode 100644 index 00000000000..c303efb8a75 --- /dev/null +++ b/test/components/views/settings/tabs/user/VoiceUserSettingsTab-test.tsx @@ -0,0 +1,56 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { mocked } from 'jest-mock'; +import { render } from '@testing-library/react'; + +import VoiceUserSettingsTab from '../../../../../../src/components/views/settings/tabs/user/VoiceUserSettingsTab'; +import MediaDeviceHandler from "../../../../../../src/MediaDeviceHandler"; + +jest.mock("../../../../../../src/MediaDeviceHandler"); +const MediaDeviceHandlerMock = mocked(MediaDeviceHandler); + +describe('', () => { + const getComponent = (): React.ReactElement => (); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders audio processing settings', () => { + const { getByTestId } = render(getComponent()); + expect(getByTestId('voice-auto-gain')).toBeTruthy(); + expect(getByTestId('voice-noise-suppression')).toBeTruthy(); + expect(getByTestId('voice-echo-cancellation')).toBeTruthy(); + }); + + it('sets and displays audio processing settings', () => { + MediaDeviceHandlerMock.getAudioAutoGainControl.mockReturnValue(false); + MediaDeviceHandlerMock.getAudioEchoCancellation.mockReturnValue(true); + MediaDeviceHandlerMock.getAudioNoiseSuppression.mockReturnValue(false); + + const { getByRole } = render(getComponent()); + + getByRole("switch", { name: "Automatically adjust the microphone volume" }).click(); + getByRole("switch", { name: "Noise suppression" }).click(); + getByRole("switch", { name: "Echo cancellation" }).click(); + + expect(MediaDeviceHandler.setAudioAutoGainControl).toHaveBeenCalledWith(true); + expect(MediaDeviceHandler.setAudioEchoCancellation).toHaveBeenCalledWith(false); + expect(MediaDeviceHandler.setAudioNoiseSuppression).toHaveBeenCalledWith(true); + }); +});