Prevent starting a voice message while recording a broadcast (#9844)
parent
d103860a94
commit
4627456837
|
@ -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,
|
||||||
|
});
|
||||||
|
};
|
|
@ -58,6 +58,8 @@ import { SendWysiwygComposer, sendMessage, getConversionFunctions } from "./wysi
|
||||||
import { MatrixClientProps, withMatrixClientHOC } from "../../../contexts/MatrixClientContext";
|
import { MatrixClientProps, withMatrixClientHOC } from "../../../contexts/MatrixClientContext";
|
||||||
import { setUpVoiceBroadcastPreRecording } from "../../../voice-broadcast/utils/setUpVoiceBroadcastPreRecording";
|
import { setUpVoiceBroadcastPreRecording } from "../../../voice-broadcast/utils/setUpVoiceBroadcastPreRecording";
|
||||||
import { SdkContextClass } from "../../../contexts/SDKContext";
|
import { SdkContextClass } from "../../../contexts/SDKContext";
|
||||||
|
import { VoiceBroadcastInfoState } from "../../../voice-broadcast";
|
||||||
|
import { createCantStartVoiceMessageBroadcastDialog } from "../dialogs/CantStartVoiceMessageBroadcastDialog";
|
||||||
|
|
||||||
let instanceCount = 0;
|
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() {
|
public render() {
|
||||||
const hasE2EIcon = Boolean(!this.state.isWysiwygLabEnabled && this.props.e2eStatus);
|
const hasE2EIcon = Boolean(!this.state.isWysiwygLabEnabled && this.props.e2eStatus);
|
||||||
const e2eIcon = hasE2EIcon && (
|
const e2eIcon = hasE2EIcon && (
|
||||||
|
@ -588,12 +604,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
isStickerPickerOpen={this.state.isStickerPickerOpen}
|
isStickerPickerOpen={this.state.isStickerPickerOpen}
|
||||||
menuPosition={menuPosition}
|
menuPosition={menuPosition}
|
||||||
relation={this.props.relation}
|
relation={this.props.relation}
|
||||||
onRecordStartEndClick={() => {
|
onRecordStartEndClick={this.onRecordStartEndClick}
|
||||||
this.voiceRecordingButton.current?.onRecordStartEndClick();
|
|
||||||
if (this.context.narrow) {
|
|
||||||
this.toggleButtonMenu();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
setStickerPickerOpen={this.setStickerPickerOpen}
|
setStickerPickerOpen={this.setStickerPickerOpen}
|
||||||
showLocationButton={!window.electron}
|
showLocationButton={!window.electron}
|
||||||
showPollsButton={this.state.showPollsButton}
|
showPollsButton={this.state.showPollsButton}
|
||||||
|
|
|
@ -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…)",
|
"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|other": "Remove %(count)s messages",
|
||||||
"Remove %(count)s messages|one": "Remove 1 message",
|
"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",
|
"Unable to load commit detail: %(msg)s": "Unable to load commit detail: %(msg)s",
|
||||||
"Unavailable": "Unavailable",
|
"Unavailable": "Unavailable",
|
||||||
"Changelog": "Changelog",
|
"Changelog": "Changelog",
|
||||||
|
|
|
@ -15,15 +15,21 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
// eslint-disable-next-line deprecate/import
|
import { EventType, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||||
import { mount, ReactWrapper } from "enzyme";
|
|
||||||
import { MatrixEvent, MsgType, RoomMember } from "matrix-js-sdk/src/matrix";
|
|
||||||
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
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 {
|
||||||
import MessageComposer, {
|
createTestClient,
|
||||||
MessageComposer as MessageComposerClass,
|
filterConsole,
|
||||||
} from "../../../../src/components/views/rooms/MessageComposer";
|
flushPromises,
|
||||||
|
mkEvent,
|
||||||
|
mkStubRoom,
|
||||||
|
mockPlatformPeg,
|
||||||
|
stubClient,
|
||||||
|
} from "../../../test-utils";
|
||||||
|
import MessageComposer from "../../../../src/components/views/rooms/MessageComposer";
|
||||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
import RoomContext from "../../../../src/contexts/RoomContext";
|
import RoomContext from "../../../../src/contexts/RoomContext";
|
||||||
|
@ -31,42 +37,108 @@ import { IRoomState } from "../../../../src/components/structures/RoomView";
|
||||||
import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
|
import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
|
||||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||||
import { LocalRoom } from "../../../../src/models/LocalRoom";
|
import { LocalRoom } from "../../../../src/models/LocalRoom";
|
||||||
import MessageComposerButtons from "../../../../src/components/views/rooms/MessageComposerButtons";
|
|
||||||
import { Features } from "../../../../src/settings/Settings";
|
import { Features } from "../../../../src/settings/Settings";
|
||||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||||
import dis from "../../../../src/dispatcher/dispatcher";
|
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 { 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 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", () => {
|
describe("MessageComposer", () => {
|
||||||
stubClient();
|
stubClient();
|
||||||
const cli = createTestClient();
|
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", () => {
|
describe("for a Room", () => {
|
||||||
const room = mkStubRoom("!roomId:server", "Room 1", cli);
|
const room = mkStubRoom("!roomId:server", "Room 1", cli);
|
||||||
|
|
||||||
it("Renders a SendMessageComposer and MessageComposerButtons by default", () => {
|
it("Renders a SendMessageComposer and MessageComposerButtons by default", () => {
|
||||||
const wrapper = wrapAndRender({ room });
|
wrapAndRender({ room });
|
||||||
|
expect(screen.getByLabelText("Send a message…")).toBeInTheDocument();
|
||||||
expect(wrapper.find("SendMessageComposer")).toHaveLength(1);
|
|
||||||
expect(wrapper.find("MessageComposerButtons")).toHaveLength(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Does not render a SendMessageComposer or MessageComposerButtons when user has no permission", () => {
|
it("Does not render a SendMessageComposer or MessageComposerButtons when user has no permission", () => {
|
||||||
const wrapper = wrapAndRender({ room }, false);
|
wrapAndRender({ room }, false);
|
||||||
|
expect(screen.queryByLabelText("Send a message…")).not.toBeInTheDocument();
|
||||||
expect(wrapper.find("SendMessageComposer")).toHaveLength(0);
|
expect(screen.getByText("You do not have permission to post to this room")).toBeInTheDocument();
|
||||||
expect(wrapper.find("MessageComposerButtons")).toHaveLength(0);
|
|
||||||
expect(wrapper.find(".mx_MessageComposer_noperm_error")).toHaveLength(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Does not render a SendMessageComposer or MessageComposerButtons when room is tombstoned", () => {
|
it("Does not render a SendMessageComposer or MessageComposerButtons when room is tombstoned", () => {
|
||||||
const wrapper = wrapAndRender(
|
wrapAndRender(
|
||||||
{ room },
|
{ room },
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
@ -81,13 +153,12 @@ describe("MessageComposer", () => {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(wrapper.find("SendMessageComposer")).toHaveLength(0);
|
expect(screen.queryByLabelText("Send a message…")).not.toBeInTheDocument();
|
||||||
expect(wrapper.find("MessageComposerButtons")).toHaveLength(0);
|
expect(screen.getByText("This room has been replaced and is no longer active.")).toBeInTheDocument();
|
||||||
expect(wrapper.find(".mx_MessageComposer_roomReplaced_header")).toHaveLength(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when receiving a »reply_to_event«", () => {
|
describe("when receiving a »reply_to_event«", () => {
|
||||||
let wrapper: ReactWrapper;
|
let roomContext: IRoomState;
|
||||||
let resizeNotifier: ResizeNotifier;
|
let resizeNotifier: ResizeNotifier;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -95,18 +166,17 @@ describe("MessageComposer", () => {
|
||||||
resizeNotifier = {
|
resizeNotifier = {
|
||||||
notifyTimelineHeightChanged: jest.fn(),
|
notifyTimelineHeightChanged: jest.fn(),
|
||||||
} as unknown as ResizeNotifier;
|
} as unknown as ResizeNotifier;
|
||||||
wrapper = wrapAndRender({
|
roomContext = wrapAndRender({
|
||||||
room,
|
room,
|
||||||
resizeNotifier,
|
resizeNotifier,
|
||||||
});
|
}).roomContext;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call notifyTimelineHeightChanged() for the same context", () => {
|
it("should call notifyTimelineHeightChanged() for the same context", () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "reply_to_event",
|
action: "reply_to_event",
|
||||||
context: (wrapper.instance as unknown as MessageComposerClass).context,
|
context: roomContext.timelineRenderingType,
|
||||||
});
|
});
|
||||||
wrapper.update();
|
|
||||||
|
|
||||||
jest.advanceTimersByTime(150);
|
jest.advanceTimersByTime(150);
|
||||||
expect(resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalled();
|
expect(resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalled();
|
||||||
|
@ -117,7 +187,6 @@ describe("MessageComposer", () => {
|
||||||
action: "reply_to_event",
|
action: "reply_to_event",
|
||||||
context: "test",
|
context: "test",
|
||||||
});
|
});
|
||||||
wrapper.update();
|
|
||||||
|
|
||||||
jest.advanceTimersByTime(150);
|
jest.advanceTimersByTime(150);
|
||||||
expect(resizeNotifier.notifyTimelineHeightChanged).not.toHaveBeenCalled();
|
expect(resizeNotifier.notifyTimelineHeightChanged).not.toHaveBeenCalled();
|
||||||
|
@ -128,28 +197,33 @@ describe("MessageComposer", () => {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
setting: "MessageComposerInput.showStickersButton",
|
setting: "MessageComposerInput.showStickersButton",
|
||||||
prop: "showStickersButton",
|
buttonLabel: "Sticker",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
setting: "MessageComposerInput.showPollsButton",
|
setting: "MessageComposerInput.showPollsButton",
|
||||||
prop: "showPollsButton",
|
buttonLabel: "Poll",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
setting: Features.VoiceBroadcast,
|
setting: Features.VoiceBroadcast,
|
||||||
prop: "showVoiceBroadcastButton",
|
buttonLabel: "Voice broadcast",
|
||||||
},
|
},
|
||||||
].forEach(({ setting, prop }) => {
|
].forEach(({ setting, buttonLabel }) => {
|
||||||
[true, false].forEach((value: boolean) => {
|
[true, false].forEach((value: boolean) => {
|
||||||
describe(`when ${setting} = ${value}`, () => {
|
describe(`when ${setting} = ${value}`, () => {
|
||||||
let wrapper: ReactWrapper;
|
beforeEach(async () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
SettingsStore.setValue(setting, null, SettingLevel.DEVICE, value);
|
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}`, () => {
|
it(`should${value || "not"} display the button`, () => {
|
||||||
expect(wrapper.find(MessageComposerButtons).props()[prop]).toBe(value);
|
if (value) {
|
||||||
|
expect(screen.getByLabelText(buttonLabel)).toBeInTheDocument();
|
||||||
|
} else {
|
||||||
|
expect(screen.queryByLabelText(buttonLabel)).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(`and setting ${setting} to ${!value}`, () => {
|
describe(`and setting ${setting} to ${!value}`, () => {
|
||||||
|
@ -164,11 +238,14 @@ describe("MessageComposer", () => {
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
wrapper.update();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should pass the prop ${prop} = ${!value}`, () => {
|
it(`should${!value || "not"} display the button`, () => {
|
||||||
expect(wrapper.find(MessageComposerButtons).props()[prop]).toBe(!value);
|
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", () => {
|
it("should not render the send button", () => {
|
||||||
const wrapper = wrapAndRender({ room });
|
wrapAndRender({ room });
|
||||||
expect(wrapper.find("SendButton")).toHaveLength(0);
|
expect(screen.queryByLabelText("Send message")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when a message has been entered", () => {
|
describe("when a message has been entered", () => {
|
||||||
let wrapper: ReactWrapper;
|
beforeEach(async () => {
|
||||||
|
const renderResult = wrapAndRender({ room }).renderResult;
|
||||||
beforeEach(() => {
|
await addTextToComposerRTL(renderResult, "Hello");
|
||||||
wrapper = wrapAndRender({ room });
|
|
||||||
addTextToComposerEnzyme(wrapper, "Hello");
|
|
||||||
wrapper.update();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render the send button", () => {
|
it("should render the send button", () => {
|
||||||
expect(wrapper.find("SendButton")).toHaveLength(1);
|
expect(screen.getByLabelText("Send message")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("UIStore interactions", () => {
|
describe("UIStore interactions", () => {
|
||||||
let wrapper: ReactWrapper;
|
|
||||||
let resizeCallback: Function;
|
let resizeCallback: Function;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -205,74 +278,74 @@ describe("MessageComposer", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when a non-resize event occurred in UIStore", () => {
|
describe("when a non-resize event occurred in UIStore", () => {
|
||||||
let stateBefore: any;
|
beforeEach(async () => {
|
||||||
|
wrapAndRender({ room });
|
||||||
beforeEach(() => {
|
await openStickerPicker();
|
||||||
wrapper = wrapAndRender({ room }).children();
|
|
||||||
stateBefore = { ...wrapper.instance().state };
|
|
||||||
resizeCallback("test", {});
|
resizeCallback("test", {});
|
||||||
wrapper.update();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not change the state", () => {
|
it("should still display the sticker picker", () => {
|
||||||
expect(wrapper.instance().state).toEqual(stateBefore);
|
expect(screen.getByText("You don't currently have any stickerpacks enabled")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when a resize to narrow event occurred in UIStore", () => {
|
describe("when a resize to narrow event occurred in UIStore", () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
wrapper = wrapAndRender({ room }, true, true).children();
|
wrapAndRender({ room }, true, true);
|
||||||
|
await openStickerPicker();
|
||||||
wrapper.setState({
|
|
||||||
isMenuOpen: true,
|
|
||||||
isStickerPickerOpen: true,
|
|
||||||
});
|
|
||||||
resizeCallback(UI_EVENTS.Resize, {});
|
resizeCallback(UI_EVENTS.Resize, {});
|
||||||
wrapper.update();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("isMenuOpen should be true", () => {
|
it("should close the menu", () => {
|
||||||
expect(wrapper.state("isMenuOpen")).toBe(true);
|
expect(screen.queryByLabelText("Sticker")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("isStickerPickerOpen should be false", () => {
|
it("should not show the attachment button", () => {
|
||||||
expect(wrapper.state("isStickerPickerOpen")).toBe(false);
|
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", () => {
|
describe("when a resize to non-narrow event occurred in UIStore", () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
wrapper = wrapAndRender({ room }, true, false).children();
|
wrapAndRender({ room }, true, false);
|
||||||
wrapper.setState({
|
await openStickerPicker();
|
||||||
isMenuOpen: true,
|
|
||||||
isStickerPickerOpen: true,
|
|
||||||
});
|
|
||||||
resizeCallback(UI_EVENTS.Resize, {});
|
resizeCallback(UI_EVENTS.Resize, {});
|
||||||
wrapper.update();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("isMenuOpen should be false", () => {
|
it("should close the menu", () => {
|
||||||
expect(wrapper.state("isMenuOpen")).toBe(false);
|
expect(screen.queryByLabelText("Sticker")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("isStickerPickerOpen should be false", () => {
|
it("should show the attachment button", () => {
|
||||||
expect(wrapper.state("isStickerPickerOpen")).toBe(false);
|
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", () => {
|
describe("when not replying to an event", () => {
|
||||||
it("should pass the expected placeholder to SendMessageComposer", () => {
|
it("should pass the expected placeholder to SendMessageComposer", () => {
|
||||||
const wrapper = wrapAndRender({ room });
|
wrapAndRender({ room });
|
||||||
expect(wrapper.find(SendMessageComposer).props().placeholder).toBe("Send a message…");
|
expect(screen.getByLabelText("Send a message…")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("and an e2e status it should pass the expected placeholder to SendMessageComposer", () => {
|
it("and an e2e status it should pass the expected placeholder to SendMessageComposer", () => {
|
||||||
const wrapper = wrapAndRender({
|
wrapAndRender({
|
||||||
room,
|
room,
|
||||||
e2eStatus: E2EStatus.Normal,
|
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) => {
|
const checkPlaceholder = (expected: string) => {
|
||||||
it("should pass the expected placeholder to SendMessageComposer", () => {
|
it("should pass the expected placeholder to SendMessageComposer", () => {
|
||||||
const wrapper = wrapAndRender(props);
|
wrapAndRender(props);
|
||||||
expect(wrapper.find(SendMessageComposer).props().placeholder).toBe(expected);
|
expect(screen.getByLabelText(expected)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -296,7 +369,7 @@ describe("MessageComposer", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
replyToEvent = mkEvent({
|
replyToEvent = mkEvent({
|
||||||
event: true,
|
event: true,
|
||||||
type: MsgType.Text,
|
type: EventType.RoomMessage,
|
||||||
user: cli.getUserId(),
|
user: cli.getUserId(),
|
||||||
content: {},
|
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", () => {
|
describe("for a LocalRoom", () => {
|
||||||
const localRoom = new LocalRoom("!room:example.com", cli, cli.getUserId()!);
|
const localRoom = new LocalRoom("!room:example.com", cli, cli.getUserId()!);
|
||||||
|
|
||||||
it("should pass the sticker picker disabled prop", () => {
|
it("should not show the stickers button", async () => {
|
||||||
const wrapper = wrapAndRender({ room: localRoom });
|
wrapAndRender({ room: localRoom });
|
||||||
expect(wrapper.find(MessageComposerButtons).props().showStickersButton).toBe(false);
|
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);
|
const room = mkStubRoom("!roomId:server", "Room 1", cli);
|
||||||
|
|
||||||
SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, true);
|
SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, true);
|
||||||
const wrapper = wrapAndRender({ room });
|
|
||||||
|
|
||||||
SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, false);
|
wrapAndRender({ room });
|
||||||
expect(wrapper.find(SendWysiwygComposer)).toBeTruthy();
|
expect(screen.getByTestId("wysiwyg-composer")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -364,7 +484,7 @@ function wrapAndRender(
|
||||||
canSendMessages = true,
|
canSendMessages = true,
|
||||||
narrow = false,
|
narrow = false,
|
||||||
tombstone?: MatrixEvent,
|
tombstone?: MatrixEvent,
|
||||||
): ReactWrapper {
|
) {
|
||||||
const mockClient = MatrixClientPeg.get();
|
const mockClient = MatrixClientPeg.get();
|
||||||
const roomId = "myroomid";
|
const roomId = "myroomid";
|
||||||
const room: any = props.room || {
|
const room: any = props.room || {
|
||||||
|
@ -376,7 +496,7 @@ function wrapAndRender(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const roomState = {
|
const roomContext = {
|
||||||
room,
|
room,
|
||||||
canSendMessages,
|
canSendMessages,
|
||||||
tombstone,
|
tombstone,
|
||||||
|
@ -389,11 +509,14 @@ function wrapAndRender(
|
||||||
permalinkCreator: new RoomPermalinkCreator(room),
|
permalinkCreator: new RoomPermalinkCreator(room),
|
||||||
};
|
};
|
||||||
|
|
||||||
return mount(
|
return {
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
renderResult: render(
|
||||||
<RoomContext.Provider value={roomState}>
|
<MatrixClientContext.Provider value={mockClient}>
|
||||||
<MessageComposer {...defaultProps} {...props} />
|
<RoomContext.Provider value={roomContext}>
|
||||||
</RoomContext.Provider>
|
<MessageComposer {...defaultProps} {...props} />
|
||||||
</MatrixClientContext.Provider>,
|
</RoomContext.Provider>
|
||||||
);
|
</MatrixClientContext.Provider>,
|
||||||
|
),
|
||||||
|
roomContext,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@ limitations under the License.
|
||||||
// eslint-disable-next-line deprecate/import
|
// eslint-disable-next-line deprecate/import
|
||||||
import { ReactWrapper } from "enzyme";
|
import { ReactWrapper } from "enzyme";
|
||||||
import { act } from "react-dom/test-utils";
|
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) =>
|
export const addTextToComposer = (container: HTMLElement, text: string) =>
|
||||||
act(() => {
|
act(() => {
|
||||||
|
@ -47,3 +48,10 @@ export const addTextToComposerEnzyme = (wrapper: ReactWrapper, text: string) =>
|
||||||
wrapper.find('[role="textbox"]').simulate("paste", pasteEvent);
|
wrapper.find('[role="textbox"]').simulate("paste", pasteEvent);
|
||||||
wrapper.update();
|
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);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue