diff --git a/src/components/views/dialogs/CantStartVoiceMessageBroadcastDialog.tsx b/src/components/views/dialogs/CantStartVoiceMessageBroadcastDialog.tsx
new file mode 100644
index 0000000000..b55598a6b5
--- /dev/null
+++ b/src/components/views/dialogs/CantStartVoiceMessageBroadcastDialog.tsx
@@ -0,0 +1,36 @@
+/*
+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 { _t } from "../../../languageHandler";
+import Modal from "../../../Modal";
+import InfoDialog from "./InfoDialog";
+
+export const createCantStartVoiceMessageBroadcastDialog = (): void => {
+    Modal.createDialog(InfoDialog, {
+        title: _t("Can't start voice message"),
+        description: (
+            <p>
+                {_t(
+                    "You can't start a voice message as you are currently recording a live broadcast. " +
+                        "Please end your live broadcast in order to start recording a voice message.",
+                )}
+            </p>
+        ),
+        hasCloseButton: true,
+    });
+};
diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx
index d7e9a5f71a..799fc98d64 100644
--- a/src/components/views/rooms/MessageComposer.tsx
+++ b/src/components/views/rooms/MessageComposer.tsx
@@ -58,6 +58,8 @@ import { SendWysiwygComposer, sendMessage, getConversionFunctions } from "./wysi
 import { MatrixClientProps, withMatrixClientHOC } from "../../../contexts/MatrixClientContext";
 import { setUpVoiceBroadcastPreRecording } from "../../../voice-broadcast/utils/setUpVoiceBroadcastPreRecording";
 import { SdkContextClass } from "../../../contexts/SDKContext";
+import { VoiceBroadcastInfoState } from "../../../voice-broadcast";
+import { createCantStartVoiceMessageBroadcastDialog } from "../dialogs/CantStartVoiceMessageBroadcastDialog";
 
 let instanceCount = 0;
 
@@ -445,6 +447,20 @@ export class MessageComposer extends React.Component<IProps, IState> {
         }
     }
 
+    private onRecordStartEndClick = (): void => {
+        const currentBroadcastRecording = SdkContextClass.instance.voiceBroadcastRecordingsStore.getCurrent();
+
+        if (currentBroadcastRecording && currentBroadcastRecording.getState() !== VoiceBroadcastInfoState.Stopped) {
+            createCantStartVoiceMessageBroadcastDialog();
+        } else {
+            this.voiceRecordingButton.current?.onRecordStartEndClick();
+        }
+
+        if (this.context.narrow) {
+            this.toggleButtonMenu();
+        }
+    };
+
     public render() {
         const hasE2EIcon = Boolean(!this.state.isWysiwygLabEnabled && this.props.e2eStatus);
         const e2eIcon = hasE2EIcon && (
@@ -588,12 +604,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
                                     isStickerPickerOpen={this.state.isStickerPickerOpen}
                                     menuPosition={menuPosition}
                                     relation={this.props.relation}
-                                    onRecordStartEndClick={() => {
-                                        this.voiceRecordingButton.current?.onRecordStartEndClick();
-                                        if (this.context.narrow) {
-                                            this.toggleButtonMenu();
-                                        }
-                                    }}
+                                    onRecordStartEndClick={this.onRecordStartEndClick}
                                     setStickerPickerOpen={this.setStickerPickerOpen}
                                     showLocationButton={!window.electron}
                                     showPollsButton={this.state.showPollsButton}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index cbefb01830..d4a8b522b8 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -2688,6 +2688,8 @@
     "Uncheck if you also want to remove system messages on this user (e.g. membership change, profile change…)": "Uncheck if you also want to remove system messages on this user (e.g. membership change, profile change…)",
     "Remove %(count)s messages|other": "Remove %(count)s messages",
     "Remove %(count)s messages|one": "Remove 1 message",
+    "Can't start voice message": "Can't start voice message",
+    "You can't start a voice message as you are currently recording a live broadcast. Please end your live broadcast in order to start recording a voice message.": "You can't start a voice message as you are currently recording a live broadcast. Please end your live broadcast in order to start recording a voice message.",
     "Unable to load commit detail: %(msg)s": "Unable to load commit detail: %(msg)s",
     "Unavailable": "Unavailable",
     "Changelog": "Changelog",
diff --git a/test/components/views/rooms/MessageComposer-test.tsx b/test/components/views/rooms/MessageComposer-test.tsx
index a20695320b..3f445b244d 100644
--- a/test/components/views/rooms/MessageComposer-test.tsx
+++ b/test/components/views/rooms/MessageComposer-test.tsx
@@ -15,15 +15,21 @@ limitations under the License.
 */
 
 import * as React from "react";
-// eslint-disable-next-line deprecate/import
-import { mount, ReactWrapper } from "enzyme";
-import { MatrixEvent, MsgType, RoomMember } from "matrix-js-sdk/src/matrix";
+import { EventType, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix";
 import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
+import { act, render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
 
-import { createTestClient, mkEvent, mkStubRoom, stubClient } from "../../../test-utils";
-import MessageComposer, {
-    MessageComposer as MessageComposerClass,
-} from "../../../../src/components/views/rooms/MessageComposer";
+import {
+    createTestClient,
+    filterConsole,
+    flushPromises,
+    mkEvent,
+    mkStubRoom,
+    mockPlatformPeg,
+    stubClient,
+} from "../../../test-utils";
+import MessageComposer from "../../../../src/components/views/rooms/MessageComposer";
 import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
 import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
 import RoomContext from "../../../../src/contexts/RoomContext";
@@ -31,42 +37,108 @@ import { IRoomState } from "../../../../src/components/structures/RoomView";
 import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
 import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
 import { LocalRoom } from "../../../../src/models/LocalRoom";
-import MessageComposerButtons from "../../../../src/components/views/rooms/MessageComposerButtons";
 import { Features } from "../../../../src/settings/Settings";
 import SettingsStore from "../../../../src/settings/SettingsStore";
 import { SettingLevel } from "../../../../src/settings/SettingLevel";
 import dis from "../../../../src/dispatcher/dispatcher";
-import { Action } from "../../../../src/dispatcher/actions";
-import { SendMessageComposer } from "../../../../src/components/views/rooms/SendMessageComposer";
 import { E2EStatus } from "../../../../src/utils/ShieldUtils";
-import { addTextToComposerEnzyme } from "../../../test-utils/composer";
+import { addTextToComposerRTL } from "../../../test-utils/composer";
 import UIStore, { UI_EVENTS } from "../../../../src/stores/UIStore";
-import { SendWysiwygComposer } from "../../../../src/components/views/rooms/wysiwyg_composer";
+import { Action } from "../../../../src/dispatcher/actions";
+import { VoiceBroadcastInfoState, VoiceBroadcastRecording } from "../../../../src/voice-broadcast";
+import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
+import { SdkContextClass } from "../../../../src/contexts/SDKContext";
+import Modal from "../../../../src/Modal";
+
+jest.mock("../../../../src/components/views/rooms/wysiwyg_composer", () => ({
+    SendWysiwygComposer: jest.fn().mockImplementation(() => <div data-testid="wysiwyg-composer" />),
+}));
+
+const openStickerPicker = async (): Promise<void> => {
+    await act(async () => {
+        await userEvent.click(screen.getByLabelText("More options"));
+        await userEvent.click(screen.getByLabelText("Sticker"));
+    });
+};
+
+const startVoiceMessage = async (): Promise<void> => {
+    await act(async () => {
+        await userEvent.click(screen.getByLabelText("More options"));
+        await userEvent.click(screen.getByLabelText("Voice Message"));
+    });
+};
+
+const setCurrentBroadcastRecording = (room: Room, state: VoiceBroadcastInfoState): void => {
+    const recording = new VoiceBroadcastRecording(
+        mkVoiceBroadcastInfoStateEvent(room.roomId, state, "@user:example.com", "ABC123"),
+        MatrixClientPeg.get(),
+        state,
+    );
+    SdkContextClass.instance.voiceBroadcastRecordingsStore.setCurrent(recording);
+};
+
+const waitForModal = async (): Promise<void> => {
+    await flushPromises();
+    await flushPromises();
+};
+
+const shouldClearModal = async (): Promise<void> => {
+    afterEach(async () => {
+        Modal.closeCurrentModal("force");
+        await waitForModal();
+    });
+};
+
+const expectVoiceMessageRecordingTriggered = (): void => {
+    // Checking for the voice message dialog text, if no mic can be found.
+    // By this we know at least that starting a voice message was triggered.
+    expect(screen.getByText("No microphone found")).toBeInTheDocument();
+};
 
 describe("MessageComposer", () => {
     stubClient();
     const cli = createTestClient();
 
+    filterConsole("Starting load of AsyncWrapper for modal");
+
+    beforeEach(() => {
+        mockPlatformPeg();
+    });
+
+    afterEach(() => {
+        jest.useRealTimers();
+
+        SdkContextClass.instance.voiceBroadcastRecordingsStore.clearCurrent();
+
+        // restore settings
+        act(() => {
+            [
+                "MessageComposerInput.showStickersButton",
+                "MessageComposerInput.showPollsButton",
+                Features.VoiceBroadcast,
+                "feature_wysiwyg_composer",
+            ].forEach((setting: string): void => {
+                SettingsStore.setValue(setting, null, SettingLevel.DEVICE, SettingsStore.getDefaultValue(setting));
+            });
+        });
+    });
+
     describe("for a Room", () => {
         const room = mkStubRoom("!roomId:server", "Room 1", cli);
 
         it("Renders a SendMessageComposer and MessageComposerButtons by default", () => {
-            const wrapper = wrapAndRender({ room });
-
-            expect(wrapper.find("SendMessageComposer")).toHaveLength(1);
-            expect(wrapper.find("MessageComposerButtons")).toHaveLength(1);
+            wrapAndRender({ room });
+            expect(screen.getByLabelText("Send a message…")).toBeInTheDocument();
         });
 
         it("Does not render a SendMessageComposer or MessageComposerButtons when user has no permission", () => {
-            const wrapper = wrapAndRender({ room }, false);
-
-            expect(wrapper.find("SendMessageComposer")).toHaveLength(0);
-            expect(wrapper.find("MessageComposerButtons")).toHaveLength(0);
-            expect(wrapper.find(".mx_MessageComposer_noperm_error")).toHaveLength(1);
+            wrapAndRender({ room }, false);
+            expect(screen.queryByLabelText("Send a message…")).not.toBeInTheDocument();
+            expect(screen.getByText("You do not have permission to post to this room")).toBeInTheDocument();
         });
 
         it("Does not render a SendMessageComposer or MessageComposerButtons when room is tombstoned", () => {
-            const wrapper = wrapAndRender(
+            wrapAndRender(
                 { room },
                 true,
                 false,
@@ -81,13 +153,12 @@ describe("MessageComposer", () => {
                 }),
             );
 
-            expect(wrapper.find("SendMessageComposer")).toHaveLength(0);
-            expect(wrapper.find("MessageComposerButtons")).toHaveLength(0);
-            expect(wrapper.find(".mx_MessageComposer_roomReplaced_header")).toHaveLength(1);
+            expect(screen.queryByLabelText("Send a message…")).not.toBeInTheDocument();
+            expect(screen.getByText("This room has been replaced and is no longer active.")).toBeInTheDocument();
         });
 
         describe("when receiving a »reply_to_event«", () => {
-            let wrapper: ReactWrapper;
+            let roomContext: IRoomState;
             let resizeNotifier: ResizeNotifier;
 
             beforeEach(() => {
@@ -95,18 +166,17 @@ describe("MessageComposer", () => {
                 resizeNotifier = {
                     notifyTimelineHeightChanged: jest.fn(),
                 } as unknown as ResizeNotifier;
-                wrapper = wrapAndRender({
+                roomContext = wrapAndRender({
                     room,
                     resizeNotifier,
-                });
+                }).roomContext;
             });
 
             it("should call notifyTimelineHeightChanged() for the same context", () => {
                 dis.dispatch({
                     action: "reply_to_event",
-                    context: (wrapper.instance as unknown as MessageComposerClass).context,
+                    context: roomContext.timelineRenderingType,
                 });
-                wrapper.update();
 
                 jest.advanceTimersByTime(150);
                 expect(resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalled();
@@ -117,7 +187,6 @@ describe("MessageComposer", () => {
                     action: "reply_to_event",
                     context: "test",
                 });
-                wrapper.update();
 
                 jest.advanceTimersByTime(150);
                 expect(resizeNotifier.notifyTimelineHeightChanged).not.toHaveBeenCalled();
@@ -128,28 +197,33 @@ describe("MessageComposer", () => {
         [
             {
                 setting: "MessageComposerInput.showStickersButton",
-                prop: "showStickersButton",
+                buttonLabel: "Sticker",
             },
             {
                 setting: "MessageComposerInput.showPollsButton",
-                prop: "showPollsButton",
+                buttonLabel: "Poll",
             },
             {
                 setting: Features.VoiceBroadcast,
-                prop: "showVoiceBroadcastButton",
+                buttonLabel: "Voice broadcast",
             },
-        ].forEach(({ setting, prop }) => {
+        ].forEach(({ setting, buttonLabel }) => {
             [true, false].forEach((value: boolean) => {
                 describe(`when ${setting} = ${value}`, () => {
-                    let wrapper: ReactWrapper;
-
-                    beforeEach(() => {
+                    beforeEach(async () => {
                         SettingsStore.setValue(setting, null, SettingLevel.DEVICE, value);
-                        wrapper = wrapAndRender({ room });
+                        wrapAndRender({ room });
+                        await act(async () => {
+                            await userEvent.click(screen.getByLabelText("More options"));
+                        });
                     });
 
-                    it(`should pass the prop ${prop} = ${value}`, () => {
-                        expect(wrapper.find(MessageComposerButtons).props()[prop]).toBe(value);
+                    it(`should${value || "not"} display the button`, () => {
+                        if (value) {
+                            expect(screen.getByLabelText(buttonLabel)).toBeInTheDocument();
+                        } else {
+                            expect(screen.queryByLabelText(buttonLabel)).not.toBeInTheDocument();
+                        }
                     });
 
                     describe(`and setting ${setting} to ${!value}`, () => {
@@ -164,11 +238,14 @@ describe("MessageComposer", () => {
                                 },
                                 true,
                             );
-                            wrapper.update();
                         });
 
-                        it(`should pass the prop ${prop} = ${!value}`, () => {
-                            expect(wrapper.find(MessageComposerButtons).props()[prop]).toBe(!value);
+                        it(`should${!value || "not"} display the button`, () => {
+                            if (!value) {
+                                expect(screen.getByLabelText(buttonLabel)).toBeInTheDocument();
+                            } else {
+                                expect(screen.queryByLabelText(buttonLabel)).not.toBeInTheDocument();
+                            }
                         });
                     });
                 });
@@ -176,26 +253,22 @@ describe("MessageComposer", () => {
         });
 
         it("should not render the send button", () => {
-            const wrapper = wrapAndRender({ room });
-            expect(wrapper.find("SendButton")).toHaveLength(0);
+            wrapAndRender({ room });
+            expect(screen.queryByLabelText("Send message")).not.toBeInTheDocument();
         });
 
         describe("when a message has been entered", () => {
-            let wrapper: ReactWrapper;
-
-            beforeEach(() => {
-                wrapper = wrapAndRender({ room });
-                addTextToComposerEnzyme(wrapper, "Hello");
-                wrapper.update();
+            beforeEach(async () => {
+                const renderResult = wrapAndRender({ room }).renderResult;
+                await addTextToComposerRTL(renderResult, "Hello");
             });
 
             it("should render the send button", () => {
-                expect(wrapper.find("SendButton")).toHaveLength(1);
+                expect(screen.getByLabelText("Send message")).toBeInTheDocument();
             });
         });
 
         describe("UIStore interactions", () => {
-            let wrapper: ReactWrapper;
             let resizeCallback: Function;
 
             beforeEach(() => {
@@ -205,74 +278,74 @@ describe("MessageComposer", () => {
             });
 
             describe("when a non-resize event occurred in UIStore", () => {
-                let stateBefore: any;
-
-                beforeEach(() => {
-                    wrapper = wrapAndRender({ room }).children();
-                    stateBefore = { ...wrapper.instance().state };
+                beforeEach(async () => {
+                    wrapAndRender({ room });
+                    await openStickerPicker();
                     resizeCallback("test", {});
-                    wrapper.update();
                 });
 
-                it("should not change the state", () => {
-                    expect(wrapper.instance().state).toEqual(stateBefore);
+                it("should still display the sticker picker", () => {
+                    expect(screen.getByText("You don't currently have any stickerpacks enabled")).toBeInTheDocument();
                 });
             });
 
             describe("when a resize to narrow event occurred in UIStore", () => {
-                beforeEach(() => {
-                    wrapper = wrapAndRender({ room }, true, true).children();
-
-                    wrapper.setState({
-                        isMenuOpen: true,
-                        isStickerPickerOpen: true,
-                    });
+                beforeEach(async () => {
+                    wrapAndRender({ room }, true, true);
+                    await openStickerPicker();
                     resizeCallback(UI_EVENTS.Resize, {});
-                    wrapper.update();
                 });
 
-                it("isMenuOpen should be true", () => {
-                    expect(wrapper.state("isMenuOpen")).toBe(true);
+                it("should close the menu", () => {
+                    expect(screen.queryByLabelText("Sticker")).not.toBeInTheDocument();
                 });
 
-                it("isStickerPickerOpen should be false", () => {
-                    expect(wrapper.state("isStickerPickerOpen")).toBe(false);
+                it("should not show the attachment button", () => {
+                    expect(screen.queryByLabelText("Attachment")).not.toBeInTheDocument();
+                });
+
+                it("should close the sticker picker", () => {
+                    expect(
+                        screen.queryByText("You don't currently have any stickerpacks enabled"),
+                    ).not.toBeInTheDocument();
                 });
             });
 
             describe("when a resize to non-narrow event occurred in UIStore", () => {
-                beforeEach(() => {
-                    wrapper = wrapAndRender({ room }, true, false).children();
-                    wrapper.setState({
-                        isMenuOpen: true,
-                        isStickerPickerOpen: true,
-                    });
+                beforeEach(async () => {
+                    wrapAndRender({ room }, true, false);
+                    await openStickerPicker();
                     resizeCallback(UI_EVENTS.Resize, {});
-                    wrapper.update();
                 });
 
-                it("isMenuOpen should be false", () => {
-                    expect(wrapper.state("isMenuOpen")).toBe(false);
+                it("should close the menu", () => {
+                    expect(screen.queryByLabelText("Sticker")).not.toBeInTheDocument();
                 });
 
-                it("isStickerPickerOpen should be false", () => {
-                    expect(wrapper.state("isStickerPickerOpen")).toBe(false);
+                it("should show the attachment button", () => {
+                    expect(screen.getByLabelText("Attachment")).toBeInTheDocument();
+                });
+
+                it("should close the sticker picker", () => {
+                    expect(
+                        screen.queryByText("You don't currently have any stickerpacks enabled"),
+                    ).not.toBeInTheDocument();
                 });
             });
         });
 
         describe("when not replying to an event", () => {
             it("should pass the expected placeholder to SendMessageComposer", () => {
-                const wrapper = wrapAndRender({ room });
-                expect(wrapper.find(SendMessageComposer).props().placeholder).toBe("Send a message…");
+                wrapAndRender({ room });
+                expect(screen.getByLabelText("Send a message…")).toBeInTheDocument();
             });
 
             it("and an e2e status it should pass the expected placeholder to SendMessageComposer", () => {
-                const wrapper = wrapAndRender({
+                wrapAndRender({
                     room,
                     e2eStatus: E2EStatus.Normal,
                 });
-                expect(wrapper.find(SendMessageComposer).props().placeholder).toBe("Send an encrypted message…");
+                expect(screen.getByLabelText("Send an encrypted message…")).toBeInTheDocument();
             });
         });
 
@@ -282,8 +355,8 @@ describe("MessageComposer", () => {
 
             const checkPlaceholder = (expected: string) => {
                 it("should pass the expected placeholder to SendMessageComposer", () => {
-                    const wrapper = wrapAndRender(props);
-                    expect(wrapper.find(SendMessageComposer).props().placeholder).toBe(expected);
+                    wrapAndRender(props);
+                    expect(screen.getByLabelText(expected)).toBeInTheDocument();
                 });
             };
 
@@ -296,7 +369,7 @@ describe("MessageComposer", () => {
             beforeEach(() => {
                 replyToEvent = mkEvent({
                     event: true,
-                    type: MsgType.Text,
+                    type: EventType.RoomMessage,
                     user: cli.getUserId(),
                     content: {},
                 });
@@ -337,25 +410,72 @@ describe("MessageComposer", () => {
                 });
             });
         });
+
+        describe("when clicking start a voice message", () => {
+            beforeEach(async () => {
+                wrapAndRender({ room });
+                await startVoiceMessage();
+                await flushPromises();
+            });
+
+            shouldClearModal();
+
+            it("should try to start a voice message", () => {
+                expectVoiceMessageRecordingTriggered();
+            });
+        });
+
+        describe("when recording a voice broadcast and trying to start a voice message", () => {
+            beforeEach(async () => {
+                setCurrentBroadcastRecording(room, VoiceBroadcastInfoState.Started);
+                wrapAndRender({ room });
+                await startVoiceMessage();
+                await waitForModal();
+            });
+
+            shouldClearModal();
+
+            it("should not start a voice message and display the info dialog", async () => {
+                expect(screen.queryByLabelText("Stop recording")).not.toBeInTheDocument();
+                expect(screen.getByText("Can't start voice message")).toBeInTheDocument();
+            });
+        });
+
+        describe("when there is a stopped voice broadcast recording and trying to start a voice message", () => {
+            beforeEach(async () => {
+                setCurrentBroadcastRecording(room, VoiceBroadcastInfoState.Stopped);
+                wrapAndRender({ room });
+                await startVoiceMessage();
+                await waitForModal();
+            });
+
+            shouldClearModal();
+
+            it("should try to start a voice message and should not display the info dialog", async () => {
+                expect(screen.queryByText("Can't start voice message")).not.toBeInTheDocument();
+                expectVoiceMessageRecordingTriggered();
+            });
+        });
     });
 
     describe("for a LocalRoom", () => {
         const localRoom = new LocalRoom("!room:example.com", cli, cli.getUserId()!);
 
-        it("should pass the sticker picker disabled prop", () => {
-            const wrapper = wrapAndRender({ room: localRoom });
-            expect(wrapper.find(MessageComposerButtons).props().showStickersButton).toBe(false);
+        it("should not show the stickers button", async () => {
+            wrapAndRender({ room: localRoom });
+            await act(async () => {
+                await userEvent.click(screen.getByLabelText("More options"));
+            });
+            expect(screen.queryByLabelText("Sticker")).not.toBeInTheDocument();
         });
     });
 
-    it("should render SendWysiwygComposer", () => {
+    it("should render SendWysiwygComposer when enabled", () => {
         const room = mkStubRoom("!roomId:server", "Room 1", cli);
-
         SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, true);
-        const wrapper = wrapAndRender({ room });
 
-        SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, false);
-        expect(wrapper.find(SendWysiwygComposer)).toBeTruthy();
+        wrapAndRender({ room });
+        expect(screen.getByTestId("wysiwyg-composer")).toBeInTheDocument();
     });
 });
 
@@ -364,7 +484,7 @@ function wrapAndRender(
     canSendMessages = true,
     narrow = false,
     tombstone?: MatrixEvent,
-): ReactWrapper {
+) {
     const mockClient = MatrixClientPeg.get();
     const roomId = "myroomid";
     const room: any = props.room || {
@@ -376,7 +496,7 @@ function wrapAndRender(
         },
     };
 
-    const roomState = {
+    const roomContext = {
         room,
         canSendMessages,
         tombstone,
@@ -389,11 +509,14 @@ function wrapAndRender(
         permalinkCreator: new RoomPermalinkCreator(room),
     };
 
-    return mount(
-        <MatrixClientContext.Provider value={mockClient}>
-            <RoomContext.Provider value={roomState}>
-                <MessageComposer {...defaultProps} {...props} />
-            </RoomContext.Provider>
-        </MatrixClientContext.Provider>,
-    );
+    return {
+        renderResult: render(
+            <MatrixClientContext.Provider value={mockClient}>
+                <RoomContext.Provider value={roomContext}>
+                    <MessageComposer {...defaultProps} {...props} />
+                </RoomContext.Provider>
+            </MatrixClientContext.Provider>,
+        ),
+        roomContext,
+    };
 }
diff --git a/test/test-utils/composer.ts b/test/test-utils/composer.ts
index 443c7a934e..7a69be62a8 100644
--- a/test/test-utils/composer.ts
+++ b/test/test-utils/composer.ts
@@ -17,7 +17,8 @@ limitations under the License.
 // eslint-disable-next-line deprecate/import
 import { ReactWrapper } from "enzyme";
 import { act } from "react-dom/test-utils";
-import { fireEvent } from "@testing-library/react";
+import { act as actRTL, fireEvent, RenderResult } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
 
 export const addTextToComposer = (container: HTMLElement, text: string) =>
     act(() => {
@@ -47,3 +48,10 @@ export const addTextToComposerEnzyme = (wrapper: ReactWrapper, text: string) =>
         wrapper.find('[role="textbox"]').simulate("paste", pasteEvent);
         wrapper.update();
     });
+
+export const addTextToComposerRTL = async (renderResult: RenderResult, text: string): Promise<void> => {
+    await actRTL(async () => {
+        await userEvent.click(renderResult.getByLabelText("Send a message…"));
+        await userEvent.keyboard(text);
+    });
+};