RTE drafts (#12674)

* Add drafts to the RTE and tests

* test drafts in threads

* lint

* Add unit test.

* Fix test failure

* Remove unused import

* Clean up wysiwyg drafts and add test.

* Fix typo

* Add timeout to allow for wasm loading.

---------

Co-authored-by: Florian Duros <florian.duros@ormaz.fr>
This commit is contained in:
David Langley 2024-08-22 13:54:01 +01:00 committed by GitHub
parent fdc5acd5a4
commit 70665d3ce3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 266 additions and 38 deletions

View file

@ -624,6 +624,18 @@ describe("<MatrixChat />", () => {
expect(localStorage.getItem(`mx_cider_state_${unknownRoomId}`)).toBeNull();
});
it("should clean up wysiwyg drafts", async () => {
Date.now = jest.fn(() => timestamp);
localStorage.setItem(`mx_wysiwyg_state_${roomId}`, "fake_content");
localStorage.setItem(`mx_wysiwyg_state_${unknownRoomId}`, "fake_content");
await getComponentAndWaitForReady();
mockClient.emit(ClientEvent.Sync, SyncState.Syncing, SyncState.Syncing);
// let things settle
await flushPromises();
expect(localStorage.getItem(`mx_wysiwyg_state_${roomId}`)).not.toBeNull();
expect(localStorage.getItem(`mx_wysiwyg_state_${unknownRoomId}`)).toBeNull();
});
it("should not clean up drafts before expiry", async () => {
// Set the last cleanup to the recent past
localStorage.setItem(`mx_cider_state_${unknownRoomId}`, "fake_content");

View file

@ -16,7 +16,7 @@ limitations under the License.
import * as React from "react";
import { EventType, MatrixEvent, Room, RoomMember, THREAD_RELATION_TYPE } from "matrix-js-sdk/src/matrix";
import { act, render, screen } from "@testing-library/react";
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {
@ -49,10 +49,6 @@ import { VoiceBroadcastInfoState, VoiceBroadcastRecording } from "../../../../sr
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
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"));
@ -76,12 +72,6 @@ const setCurrentBroadcastRecording = (room: Room, state: VoiceBroadcastInfoState
SdkContextClass.instance.voiceBroadcastRecordingsStore.setCurrent(recording);
};
const shouldClearModal = async (): Promise<void> => {
afterEach(async () => {
await clearAllModals();
});
};
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.
@ -96,7 +86,8 @@ describe("MessageComposer", () => {
mockPlatformPeg();
});
afterEach(() => {
afterEach(async () => {
await clearAllModals();
jest.useRealTimers();
SdkContextClass.instance.voiceBroadcastRecordingsStore.clearCurrent();
@ -415,8 +406,6 @@ describe("MessageComposer", () => {
await flushPromises();
});
shouldClearModal();
it("should try to start a voice message", () => {
expectVoiceMessageRecordingTriggered();
});
@ -430,8 +419,6 @@ describe("MessageComposer", () => {
await waitEnoughCyclesForModal();
});
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();
@ -446,8 +433,6 @@ describe("MessageComposer", () => {
await waitEnoughCyclesForModal();
});
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();
@ -467,13 +452,50 @@ describe("MessageComposer", () => {
});
});
it("should render SendWysiwygComposer when enabled", () => {
it("wysiwyg correctly persists state to and from localStorage", async () => {
const room = mkStubRoom("!roomId:server", "Room 1", cli);
SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, true);
const messageText = "Test Text";
await SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, true);
const { renderResult, rawComponent } = wrapAndRender({ room });
const { unmount, rerender } = renderResult;
wrapAndRender({ room });
expect(screen.getByTestId("wysiwyg-composer")).toBeInTheDocument();
});
await act(async () => {
await flushPromises();
});
const key = `mx_wysiwyg_state_${room.roomId}`;
await act(async () => {
await userEvent.click(screen.getByRole("textbox"));
});
fireEvent.input(screen.getByRole("textbox"), {
data: messageText,
inputType: "insertText",
});
await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(messageText));
// Wait for event dispatch to happen
await act(async () => {
await flushPromises();
});
// assert there is state persisted
expect(localStorage.getItem(key)).toBeNull();
// ensure the right state was persisted to localStorage
unmount();
// assert the persisted state
expect(JSON.parse(localStorage.getItem(key)!)).toStrictEqual({
content: messageText,
isRichText: true,
});
// ensure the correct state is re-loaded
rerender(rawComponent);
await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(messageText));
}, 10000);
});
function wrapAndRender(
@ -506,14 +528,16 @@ function wrapAndRender(
permalinkCreator: new RoomPermalinkCreator(room),
};
const getRawComponent = (props = {}, context = roomContext, client = mockClient) => (
<MatrixClientContext.Provider value={client}>
<RoomContext.Provider value={context}>
<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>,
),
rawComponent: getRawComponent(props, roomContext, mockClient),
renderResult: render(getRawComponent(props, roomContext, mockClient)),
roomContext,
};
}