Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Add Keyboard Up test
Browse files Browse the repository at this point in the history
  • Loading branch information
florianduros committed Jan 27, 2023
1 parent d5a218b commit 3d5d377
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ export function isCaretAtEnd(editor: HTMLElement): boolean {
return false;
}

// When we are going cycling across all the timeline message with the keyboard
// The caret is on the last message element but the focusNode and the anchorNode is the editor himself instead of the text in it.
// When we are cycling across all the timeline message with the keyboard
// The caret is on the last text element but focusNode and anchorNode refers to the editor div
// In this case, the focusOffset & anchorOffset match the index + 1 of the selected text
const isOnLastElement = selection.focusNode === editor && selection.focusOffset === editor.childNodes?.length;
if (isOnLastElement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ import RoomContext from "../../../../../src/contexts/RoomContext";
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../../src/dispatcher/actions";
import { IRoomState } from "../../../../../src/components/structures/RoomView";
import { createTestClient, flushPromises, getRoomContext, mkEvent, mkStubRoom } from "../../../../test-utils";
import {
createTestClient,
flushPromises,
getRoomContext,
mkEvent,
mkStubRoom,
mockPlatformPeg,
} from "../../../../test-utils";
import { EditWysiwygComposer } from "../../../../../src/components/views/rooms/wysiwyg_composer";
import EditorStateTransfer from "../../../../../src/utils/EditorStateTransfer";
import { Emoji } from "../../../../../src/components/views/rooms/wysiwyg_composer/components/Emoji";
Expand All @@ -32,38 +39,55 @@ import dis from "../../../../../src/dispatcher/dispatcher";
import { ComposerInsertPayload, ComposerType } from "../../../../../src/dispatcher/payloads/ComposerInsertPayload";
import { ActionPayload } from "../../../../../src/dispatcher/payloads";
import * as EmojiButton from "../../../../../src/components/views/rooms/EmojiButton";
import { setSelection } from "../../../../../src/components/views/rooms/wysiwyg_composer/utils/selection";
import { EventTimeline } from "../../../../../../matrix-js-sdk";
import * as EventUtils from "../../../../../src/utils/EventUtils";
import { SubSelection } from "../../../../../src/components/views/rooms/wysiwyg_composer/types";

describe("EditWysiwygComposer", () => {
afterEach(() => {
jest.resetAllMocks();
});

const mockClient = createTestClient();
const mockEvent = mkEvent({
type: "m.room.message",
room: "myfakeroom",
user: "myfakeuser",
content: {
msgtype: "m.text",
body: "Replying to this",
format: "org.matrix.custom.html",
formatted_body: "Replying <b>to</b> this new content",
},
event: true,
});
const mockRoom = mkStubRoom("myfakeroom", "myfakeroom", mockClient) as any;
mockRoom.findEventById = jest.fn((eventId) => {
return eventId === mockEvent.getId() ? mockEvent : null;
});
function createMocks(eventContent = "Replying <strong>to</strong> this new content") {
const mockClient = createTestClient();
const mockEvent = mkEvent({
type: "m.room.message",
room: "myfakeroom",
user: "myfakeuser",
content: {
msgtype: "m.text",
body: "Replying to this",
format: "org.matrix.custom.html",
formatted_body: eventContent,
},
event: true,
});
const mockRoom = mkStubRoom("myfakeroom", "myfakeroom", mockClient) as any;
mockRoom.findEventById = jest.fn((eventId) => {
return eventId === mockEvent.getId() ? mockEvent : null;
});

const defaultRoomContext: IRoomState = getRoomContext(mockRoom, {
liveTimeline: { getEvents: () => [] } as unknown as EventTimeline,
});

const editorStateTransfer = new EditorStateTransfer(mockEvent);

const defaultRoomContext: IRoomState = getRoomContext(mockRoom, {});
return { defaultRoomContext, editorStateTransfer, mockClient, mockEvent };
}

const editorStateTransfer = new EditorStateTransfer(mockEvent);
const { editorStateTransfer, defaultRoomContext, mockClient, mockEvent } = createMocks();

const customRender = (disabled = false, _editorStateTransfer = editorStateTransfer) => {
const customRender = (
disabled = false,
_editorStateTransfer = editorStateTransfer,
client = mockClient,
roomContext = defaultRoomContext,
) => {
return render(
<MatrixClientContext.Provider value={mockClient}>
<RoomContext.Provider value={defaultRoomContext}>
<MatrixClientContext.Provider value={client}>
<RoomContext.Provider value={roomContext}>
<EditWysiwygComposer disabled={disabled} editorStateTransfer={_editorStateTransfer} />
</RoomContext.Provider>
</MatrixClientContext.Provider>,
Expand Down Expand Up @@ -176,12 +200,14 @@ describe("EditWysiwygComposer", () => {
});

describe("Edit and save actions", () => {
let spyDispatcher: jest.SpyInstance<void, [payload: ActionPayload, sync?: boolean]>;
beforeEach(async () => {
spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");
customRender();
await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true"));
});

const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");
// const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");
afterEach(() => {
spyDispatcher.mockRestore();
});
Expand All @@ -204,7 +230,7 @@ describe("EditWysiwygComposer", () => {

it("Should send message on save button click", async () => {
// When
const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");
// const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");
fireEvent.input(screen.getByRole("textbox"), {
data: "foo bar",
inputType: "insertText",
Expand Down Expand Up @@ -318,4 +344,141 @@ describe("EditWysiwygComposer", () => {
await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(/🦫/));
dis.unregister(dispatcherRef);
});

describe("Keyboard navigation", () => {
const setup = async (
editorState = editorStateTransfer,
client = createTestClient(),
roomContext = defaultRoomContext,
) => {
const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch");
customRender(false, editorState, client, roomContext);
await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true"));
return { textbox: screen.getByRole("textbox"), spyDispatcher };
};

beforeEach(() => {
mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) });
jest.spyOn(EventUtils, "findEditableEvent").mockReturnValue(mockEvent);
});

function select(selection: SubSelection) {
return act(async () => {
await setSelection(selection);
// the event is not automatically fired by jest
document.dispatchEvent(new CustomEvent("selectionchange"));
});
}

describe("Moving up", () => {
it("Should not moving when caret is not at beginning of the text", async () => {
// When
const { textbox, spyDispatcher } = await setup();
const textNode = textbox.firstChild;
await select({
anchorNode: textNode,
anchorOffset: 1,
focusNode: textNode,
focusOffset: 2,
isForward: true,
});

fireEvent.keyDown(textbox, {
key: "ArrowUp",
});

// Then
expect(spyDispatcher).toBeCalledTimes(0);
});

it("Should not moving when the content has changed", async () => {
// When
const { textbox, spyDispatcher } = await setup();
fireEvent.input(textbox, {
data: "word",
inputType: "insertText",
});
const textNode = textbox.firstChild;
await select({
anchorNode: textNode,
anchorOffset: 0,
focusNode: textNode,
focusOffset: 0,
isForward: true,
});

fireEvent.keyDown(textbox, {
key: "ArrowUp",
});

// Then
expect(spyDispatcher).toBeCalledTimes(0);
});

it("Should moving up", async () => {
// When
const { textbox, spyDispatcher } = await setup();
const textNode = textbox.firstChild;
await select({
anchorNode: textNode,
anchorOffset: 0,
focusNode: textNode,
focusOffset: 0,
isForward: true,
});

fireEvent.keyDown(textbox, {
key: "ArrowUp",
});

// Wait for event dispatch to happen
await act(async () => {
await flushPromises();
});

// Then
await waitFor(() =>
expect(spyDispatcher).toBeCalledWith({
action: Action.EditEvent,
event: mockEvent,
timelineRenderingType: defaultRoomContext.timelineRenderingType,
}),
);
});

it("Should moving up in list", async () => {
// When
const { mockEvent, defaultRoomContext, mockClient, editorStateTransfer } = createMocks(
"<ul><li><strong>Content</strong></li><li>Other Content</li></ul>",
);
jest.spyOn(EventUtils, "findEditableEvent").mockReturnValue(mockEvent);
const { textbox, spyDispatcher } = await setup(editorStateTransfer, mockClient, defaultRoomContext);

const textNode = textbox.firstChild;
await select({
anchorNode: textNode,
anchorOffset: 0,
focusNode: textNode,
focusOffset: 0,
isForward: true,
});

fireEvent.keyDown(textbox, {
key: "ArrowUp",
});

// Wait for event dispatch to happen
await act(async () => {
await flushPromises();
});

// Then
expect(spyDispatcher).toBeCalledWith({
action: Action.EditEvent,
event: mockEvent,
timelineRenderingType: defaultRoomContext.timelineRenderingType,
});
});
});
});
});

0 comments on commit 3d5d377

Please sign in to comment.