This reverts commit 1af71089dd
.
This commit is contained in:
parent
58a4003a59
commit
63678603e0
11 changed files with 23 additions and 585 deletions
|
@ -93,15 +93,15 @@ describe("SendWysiwygComposer", () => {
|
|||
customRender(jest.fn(), jest.fn(), false, true);
|
||||
|
||||
// Then
|
||||
expect(await screen.findByTestId("WysiwygComposer")).toBeInTheDocument();
|
||||
await waitFor(() => expect(screen.getByTestId("WysiwygComposer")).toBeTruthy());
|
||||
});
|
||||
|
||||
it("Should render PlainTextComposer when isRichTextEnabled is at false", async () => {
|
||||
it("Should render PlainTextComposer when isRichTextEnabled is at false", () => {
|
||||
// When
|
||||
customRender(jest.fn(), jest.fn(), false, false);
|
||||
|
||||
// Then
|
||||
expect(await screen.findByTestId("PlainTextComposer")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("PlainTextComposer")).toBeTruthy();
|
||||
});
|
||||
|
||||
describe.each([{ isRichTextEnabled: true }, { isRichTextEnabled: false }])(
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
Copyright 2023 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 "@testing-library/jest-dom";
|
||||
import React, { createRef } from "react";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
|
||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext from "../../../../../../src/contexts/RoomContext";
|
||||
import { WysiwygAutocomplete } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/WysiwygAutocomplete";
|
||||
import { getRoomContext, mkStubRoom, stubClient } from "../../../../../test-utils";
|
||||
import Autocomplete from "../../../../../../src/components/views/rooms/Autocomplete";
|
||||
import Autocompleter, { ICompletion } from "../../../../../../src/autocomplete/Autocompleter";
|
||||
import AutocompleteProvider from "../../../../../../src/autocomplete/AutocompleteProvider";
|
||||
|
||||
const mockCompletion: ICompletion[] = [
|
||||
{
|
||||
type: "user",
|
||||
completion: "user_1",
|
||||
completionId: "@user_1:host.local",
|
||||
range: { start: 1, end: 1 },
|
||||
component: <div>user_1</div>,
|
||||
},
|
||||
{
|
||||
type: "user",
|
||||
completion: "user_2",
|
||||
completionId: "@user_2:host.local",
|
||||
range: { start: 1, end: 1 },
|
||||
component: <div>user_2</div>,
|
||||
},
|
||||
];
|
||||
|
||||
const constructMockProvider = (data: ICompletion[]) =>
|
||||
({
|
||||
getCompletions: jest.fn().mockImplementation(async () => data),
|
||||
getName: jest.fn().mockReturnValue("test provider"),
|
||||
renderCompletions: jest.fn().mockImplementation((components) => components),
|
||||
} as unknown as AutocompleteProvider);
|
||||
|
||||
describe("WysiwygAutocomplete", () => {
|
||||
beforeAll(() => {
|
||||
// scrollTo not implemented in JSDOM
|
||||
window.HTMLElement.prototype.scrollTo = function () {};
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const autocompleteRef = createRef<Autocomplete>();
|
||||
const getCompletionsSpy = jest.spyOn(Autocompleter.prototype, "getCompletions").mockResolvedValue([
|
||||
{
|
||||
completions: mockCompletion,
|
||||
provider: constructMockProvider(mockCompletion),
|
||||
command: { command: ["truthy"] as RegExpExecArray }, // needed for us to unhide the autocomplete when testing
|
||||
},
|
||||
]);
|
||||
const mockHandleMention = jest.fn();
|
||||
|
||||
const renderComponent = (props = {}) => {
|
||||
const mockClient = stubClient();
|
||||
const mockRoom = mkStubRoom("test_room", "test_room", mockClient);
|
||||
const mockRoomContext = getRoomContext(mockRoom, {});
|
||||
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={mockRoomContext}>
|
||||
<WysiwygAutocomplete
|
||||
ref={autocompleteRef}
|
||||
suggestion={null}
|
||||
handleMention={mockHandleMention}
|
||||
{...props}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
it("does not show the autocomplete when room is undefined", () => {
|
||||
render(<WysiwygAutocomplete ref={autocompleteRef} suggestion={null} handleMention={mockHandleMention} />);
|
||||
expect(screen.queryByTestId("autocomplete-wrapper")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not call for suggestions with a null suggestion prop", async () => {
|
||||
// render the component, the default props have suggestion = null
|
||||
renderComponent();
|
||||
|
||||
// check that getCompletions is not called, and we have no suggestions
|
||||
expect(getCompletionsSpy).not.toHaveBeenCalled();
|
||||
expect(screen.queryByRole("presentation")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls getCompletions when given a valid suggestion prop", async () => {
|
||||
renderComponent({ suggestion: { keyChar: "@", text: "abc", type: "mention" } });
|
||||
|
||||
// wait for getCompletions to have been called
|
||||
await waitFor(() => {
|
||||
expect(getCompletionsSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// check that some suggestions are shown
|
||||
expect(screen.getByRole("presentation")).toBeInTheDocument();
|
||||
|
||||
// and that they are the mock completions
|
||||
mockCompletion.forEach(({ completion }) => expect(screen.getByText(completion)).toBeInTheDocument());
|
||||
});
|
||||
});
|
|
@ -21,7 +21,7 @@ import userEvent from "@testing-library/user-event";
|
|||
|
||||
import { WysiwygComposer } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer";
|
||||
import SettingsStore from "../../../../../../src/settings/SettingsStore";
|
||||
import { flushPromises, mockPlatformPeg, stubClient, mkStubRoom } from "../../../../../test-utils";
|
||||
import { createTestClient, flushPromises, mockPlatformPeg } from "../../../../../test-utils";
|
||||
import defaultDispatcher from "../../../../../../src/dispatcher/dispatcher";
|
||||
import * as EventUtils from "../../../../../../src/utils/EventUtils";
|
||||
import { Action } from "../../../../../../src/dispatcher/actions";
|
||||
|
@ -36,25 +36,11 @@ import EditorStateTransfer from "../../../../../../src/utils/EditorStateTransfer
|
|||
import { SubSelection } from "../../../../../../src/components/views/rooms/wysiwyg_composer/types";
|
||||
import { setSelection } from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/selection";
|
||||
import { parseEditorStateTransfer } from "../../../../../../src/components/views/rooms/wysiwyg_composer/hooks/useInitialContent";
|
||||
import Autocompleter, { ICompletion } from "../../../../../../src/autocomplete/Autocompleter";
|
||||
import AutocompleteProvider from "../../../../../../src/autocomplete/AutocompleteProvider";
|
||||
import * as Permalinks from "../../../../../../src/utils/permalinks/Permalinks";
|
||||
import { PermalinkParts } from "../../../../../../src/utils/permalinks/PermalinkConstructor";
|
||||
|
||||
describe("WysiwygComposer", () => {
|
||||
const customRender = (onChange = jest.fn(), onSend = jest.fn(), disabled = false, initialContent?: string) => {
|
||||
const { mockClient, defaultRoomContext } = createMocks();
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={defaultRoomContext}>
|
||||
<WysiwygComposer
|
||||
onChange={onChange}
|
||||
onSend={onSend}
|
||||
disabled={disabled}
|
||||
initialContent={initialContent}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</MatrixClientContext.Provider>,
|
||||
<WysiwygComposer onChange={onChange} onSend={onSend} disabled={disabled} initialContent={initialContent} />,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -62,12 +48,12 @@ describe("WysiwygComposer", () => {
|
|||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("Should have contentEditable at false when disabled", async () => {
|
||||
it("Should have contentEditable at false when disabled", () => {
|
||||
// When
|
||||
customRender(jest.fn(), jest.fn(), true);
|
||||
|
||||
// Then
|
||||
await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "false"));
|
||||
expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "false");
|
||||
});
|
||||
|
||||
describe("Standard behavior", () => {
|
||||
|
@ -158,199 +144,6 @@ describe("WysiwygComposer", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("Mentions", () => {
|
||||
const dispatchSpy = jest.spyOn(defaultDispatcher, "dispatch");
|
||||
|
||||
const mockCompletions: ICompletion[] = [
|
||||
{
|
||||
type: "user",
|
||||
href: "www.user1.com",
|
||||
completion: "user_1",
|
||||
completionId: "@user_1:host.local",
|
||||
range: { start: 1, end: 1 },
|
||||
component: <div>user_1</div>,
|
||||
},
|
||||
{
|
||||
type: "user",
|
||||
href: "www.user2.com",
|
||||
completion: "user_2",
|
||||
completionId: "@user_2:host.local",
|
||||
range: { start: 1, end: 1 },
|
||||
component: <div>user_2</div>,
|
||||
},
|
||||
{
|
||||
// no href user
|
||||
type: "user",
|
||||
completion: "user_without_href",
|
||||
completionId: "@user_3:host.local",
|
||||
range: { start: 1, end: 1 },
|
||||
component: <div>user_without_href</div>,
|
||||
},
|
||||
{
|
||||
type: "room",
|
||||
href: "www.room1.com",
|
||||
completion: "#room_with_completion_id",
|
||||
completionId: "@room_1:host.local",
|
||||
range: { start: 1, end: 1 },
|
||||
component: <div>room_with_completion_id</div>,
|
||||
},
|
||||
{
|
||||
type: "room",
|
||||
href: "www.room2.com",
|
||||
completion: "#room_without_completion_id",
|
||||
range: { start: 1, end: 1 },
|
||||
component: <div>room_without_completion_id</div>,
|
||||
},
|
||||
];
|
||||
|
||||
const constructMockProvider = (data: ICompletion[]) =>
|
||||
({
|
||||
getCompletions: jest.fn().mockImplementation(async () => data),
|
||||
getName: jest.fn().mockReturnValue("test provider"),
|
||||
renderCompletions: jest.fn().mockImplementation((components) => components),
|
||||
} as unknown as AutocompleteProvider);
|
||||
|
||||
// for each test we will insert input simulating a user mention
|
||||
const insertMentionInput = async () => {
|
||||
fireEvent.input(screen.getByRole("textbox"), {
|
||||
data: "@abc",
|
||||
inputType: "insertText",
|
||||
});
|
||||
|
||||
// the autocomplete suggestions container has the presentation role, wait for it to be present
|
||||
expect(await screen.findByRole("presentation")).toBeInTheDocument();
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
// setup the required spies
|
||||
jest.spyOn(Autocompleter.prototype, "getCompletions").mockResolvedValue([
|
||||
{
|
||||
completions: mockCompletions,
|
||||
provider: constructMockProvider(mockCompletions),
|
||||
command: { command: ["truthy"] as RegExpExecArray }, // needed for us to unhide the autocomplete when testing
|
||||
},
|
||||
]);
|
||||
jest.spyOn(Permalinks, "parsePermalink").mockReturnValue({
|
||||
userId: "mockParsedUserId",
|
||||
} as unknown as PermalinkParts);
|
||||
|
||||
// then render the component and wait for the composer to be ready
|
||||
customRender();
|
||||
await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it("shows the autocomplete when text has @ prefix and autoselects the first item", async () => {
|
||||
await insertMentionInput();
|
||||
expect(screen.getByText(mockCompletions[0].completion)).toHaveAttribute("aria-selected", "true");
|
||||
});
|
||||
|
||||
it("pressing up and down arrows allows us to change the autocomplete selection", async () => {
|
||||
await insertMentionInput();
|
||||
|
||||
// press the down arrow - nb using .keyboard allows us to not have to specify a node, which
|
||||
// means that we know the autocomplete is correctly catching the event
|
||||
await userEvent.keyboard("{ArrowDown}");
|
||||
expect(screen.getByText(mockCompletions[0].completion)).toHaveAttribute("aria-selected", "false");
|
||||
expect(screen.getByText(mockCompletions[1].completion)).toHaveAttribute("aria-selected", "true");
|
||||
|
||||
// reverse the process and check again
|
||||
await userEvent.keyboard("{ArrowUp}");
|
||||
expect(screen.getByText(mockCompletions[0].completion)).toHaveAttribute("aria-selected", "true");
|
||||
expect(screen.getByText(mockCompletions[1].completion)).toHaveAttribute("aria-selected", "false");
|
||||
});
|
||||
|
||||
it("pressing enter selects the mention and inserts it into the composer as a link", async () => {
|
||||
await insertMentionInput();
|
||||
|
||||
// press enter
|
||||
await userEvent.keyboard("{Enter}");
|
||||
|
||||
// check that it closes the autocomplete
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("presentation")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
// check that it inserts the completion text as a link
|
||||
expect(screen.getByRole("link", { name: mockCompletions[0].completion })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("clicking on a mention in the composer dispatches the correct action", async () => {
|
||||
await insertMentionInput();
|
||||
|
||||
// press enter
|
||||
await userEvent.keyboard("{Enter}");
|
||||
|
||||
// check that it closes the autocomplete
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("presentation")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
// click on the user mention link that has been inserted
|
||||
await userEvent.click(screen.getByRole("link", { name: mockCompletions[0].completion }));
|
||||
expect(dispatchSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
// this relies on the output from the mock function in mkStubRoom
|
||||
expect(dispatchSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
action: Action.ViewUser,
|
||||
member: expect.objectContaining({
|
||||
userId: mkStubRoom(undefined, undefined, undefined).getMember("any")?.userId,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("selecting a mention without a href closes the autocomplete but does not insert a mention", async () => {
|
||||
await insertMentionInput();
|
||||
|
||||
// select the relevant user by clicking
|
||||
await userEvent.click(screen.getByText("user_without_href"));
|
||||
|
||||
// check that it closes the autocomplete
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("presentation")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
// check that it has not inserted a link
|
||||
expect(screen.queryByRole("link", { name: "user_without_href" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("selecting a room mention with a completionId uses client.getRoom", async () => {
|
||||
await insertMentionInput();
|
||||
|
||||
// select the room suggestion by clicking
|
||||
await userEvent.click(screen.getByText("room_with_completion_id"));
|
||||
|
||||
// check that it closes the autocomplete
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("presentation")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
// check that it has inserted a link and looked up the name from the mock client
|
||||
// which will always return 'My room'
|
||||
expect(screen.getByRole("link", { name: "My room" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("selecting a room mention without a completionId uses client.getRooms", async () => {
|
||||
await insertMentionInput();
|
||||
|
||||
// select the room suggestion
|
||||
await userEvent.click(screen.getByText("room_without_completion_id"));
|
||||
|
||||
// check that it closes the autocomplete
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("presentation")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
// check that it has inserted a link and falls back to the completion text
|
||||
expect(screen.getByRole("link", { name: "#room_without_completion_id" })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("When settings require Ctrl+Enter to send", () => {
|
||||
const onChange = jest.fn();
|
||||
const onSend = jest.fn();
|
||||
|
@ -448,11 +241,10 @@ describe("WysiwygComposer", () => {
|
|||
|
||||
const setup = async (
|
||||
editorState?: EditorStateTransfer,
|
||||
client = stubClient(),
|
||||
client = createTestClient(),
|
||||
roomContext = defaultRoomContext,
|
||||
) => {
|
||||
const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");
|
||||
|
||||
customRender(client, roomContext, editorState);
|
||||
await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true"));
|
||||
return { textbox: screen.getByRole("textbox"), spyDispatcher };
|
||||
|
|
|
@ -16,12 +16,12 @@ limitations under the License.
|
|||
|
||||
import { EventTimeline, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { getRoomContext, mkEvent, mkStubRoom, stubClient } from "../../../../test-utils";
|
||||
import { createTestClient, getRoomContext, mkEvent, mkStubRoom } from "../../../../test-utils";
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import EditorStateTransfer from "../../../../../src/utils/EditorStateTransfer";
|
||||
|
||||
export function createMocks(eventContent = "Replying <strong>to</strong> this new content") {
|
||||
const mockClient = stubClient();
|
||||
const mockClient = createTestClient();
|
||||
const mockEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
room: "myfakeroom",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue