1c66f0ba01
* refactor: extract `getTagsForRoom` from old rls `getTagsForRoom` doesn't rely on the rls state. We can extract it safely to an external function. The function is not moved into the rls v3 because the rls is for the room list and not for other ui elements. * refactor: remove dead code * test: add more tests for `getTagsForRoom` * refactor: `getTagsForRoom` in old rls * doc: add missing tsdoc for `room`
266 lines
9.4 KiB
TypeScript
266 lines
9.4 KiB
TypeScript
/*
|
|
* Copyright 2025 New Vector Ltd.
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
* Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import { act, renderHook, waitFor } from "jest-matrix-react";
|
|
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
|
|
|
|
import { useRoomSummaryCardViewModel } from "../../../../../src/components/viewmodels/right_panel/RoomSummaryCardViewModel";
|
|
import { mkStubRoom, stubClient, withClientContextRenderOptions } from "../../../../test-utils";
|
|
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
|
import { DefaultTagID } from "../../../../../src/stores/room-list-v3/skip-list/tag";
|
|
import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelStore";
|
|
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases";
|
|
import Modal from "../../../../../src/Modal";
|
|
import { ShareDialog } from "../../../../../src/components/views/dialogs/ShareDialog";
|
|
import ExportDialog from "../../../../../src/components/views/dialogs/ExportDialog";
|
|
import { PollHistoryDialog } from "../../../../../src/components/views/dialogs/PollHistoryDialog";
|
|
import { ReportRoomDialog } from "../../../../../src/components/views/dialogs/ReportRoomDialog";
|
|
import { inviteToRoom } from "../../../../../src/utils/room/inviteToRoom";
|
|
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
|
import * as hooks from "../../../../../src/hooks/useAccountData";
|
|
import * as getTagsForRoomUtils from "../../../../../src/utils/room/getTagsForRoom";
|
|
|
|
jest.mock("../../../../../src/utils/room/inviteToRoom", () => ({
|
|
inviteToRoom: jest.fn(),
|
|
}));
|
|
|
|
describe("useRoomSummaryCardViewModel", () => {
|
|
let matrixClient: MatrixClient;
|
|
let room: Room;
|
|
let permalinkCreator: any;
|
|
const onSearchCancel = jest.fn();
|
|
|
|
beforeEach(() => {
|
|
matrixClient = stubClient();
|
|
room = mkStubRoom("roomId", "roomName", matrixClient);
|
|
permalinkCreator = {};
|
|
|
|
DMRoomMap.setShared({
|
|
getUserIdForRoomId: jest.fn(),
|
|
} as unknown as DMRoomMap);
|
|
|
|
jest.spyOn(getTagsForRoomUtils, "getTagsForRoom").mockReturnValue([]);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.resetAllMocks();
|
|
});
|
|
|
|
function render() {
|
|
return renderHook(
|
|
() => useRoomSummaryCardViewModel(room, permalinkCreator, onSearchCancel),
|
|
withClientContextRenderOptions(matrixClient),
|
|
);
|
|
}
|
|
|
|
it("should return correct initial state", () => {
|
|
const { result } = render();
|
|
|
|
expect(result.current.isDirectMessage).toBe(false);
|
|
expect(result.current.isRoomEncrypted).toBe(false);
|
|
expect(result.current.isVideoRoom).toBe(false);
|
|
expect(result.current.isFavorite).toBe(false);
|
|
expect(result.current.pinCount).toBe(0);
|
|
expect(result.current.searchInputRef.current).toBe(null);
|
|
});
|
|
|
|
it("should handle room members click", () => {
|
|
const spy = jest.spyOn(RightPanelStore.instance, "pushCard");
|
|
const { result } = render();
|
|
|
|
result.current.onRoomMembersClick();
|
|
expect(spy).toHaveBeenCalledWith({ phase: RightPanelPhases.MemberList }, true);
|
|
});
|
|
|
|
it("should handle room settings click", () => {
|
|
const spy = jest.spyOn(defaultDispatcher, "dispatch");
|
|
const { result } = render();
|
|
|
|
result.current.onRoomSettingsClick(new Event("click"));
|
|
expect(spy).toHaveBeenCalledWith({ action: "open_room_settings" });
|
|
});
|
|
|
|
it("should handle leave room click", () => {
|
|
const spy = jest.spyOn(defaultDispatcher, "dispatch");
|
|
const { result } = render();
|
|
|
|
result.current.onLeaveRoomClick();
|
|
expect(spy).toHaveBeenCalledWith({
|
|
action: "leave_room",
|
|
room_id: room.roomId,
|
|
});
|
|
});
|
|
|
|
it("should handle room threads click", () => {
|
|
const spy = jest.spyOn(RightPanelStore.instance, "pushCard");
|
|
const { result } = render();
|
|
|
|
result.current.onRoomThreadsClick();
|
|
expect(spy).toHaveBeenCalledWith({ phase: RightPanelPhases.ThreadPanel }, true);
|
|
});
|
|
|
|
it("should handle room files click", () => {
|
|
const spy = jest.spyOn(RightPanelStore.instance, "pushCard");
|
|
const { result } = render();
|
|
|
|
result.current.onRoomFilesClick();
|
|
expect(spy).toHaveBeenCalledWith({ phase: RightPanelPhases.FilePanel }, true);
|
|
});
|
|
|
|
it("should handle room extensions click", () => {
|
|
const spy = jest.spyOn(RightPanelStore.instance, "pushCard");
|
|
const { result } = render();
|
|
|
|
result.current.onRoomExtensionsClick();
|
|
expect(spy).toHaveBeenCalledWith({ phase: RightPanelPhases.Extensions }, true);
|
|
});
|
|
|
|
it("should handle room pins click", () => {
|
|
const spy = jest.spyOn(RightPanelStore.instance, "pushCard");
|
|
const { result } = render();
|
|
|
|
result.current.onRoomPinsClick();
|
|
expect(spy).toHaveBeenCalledWith({ phase: RightPanelPhases.PinnedMessages }, true);
|
|
});
|
|
|
|
it("should handle room invite click", () => {
|
|
const { result } = render();
|
|
|
|
result.current.onInviteToRoomClick();
|
|
expect(inviteToRoom).toHaveBeenCalledWith(room);
|
|
});
|
|
|
|
describe("action that trigger a dialog", () => {
|
|
let createDialogSpy: jest.SpyInstance;
|
|
|
|
beforeEach(() => {
|
|
createDialogSpy = jest.spyOn(Modal, "createDialog").mockImplementation(() => ({
|
|
finished: Promise.resolve([false]),
|
|
close: jest.fn(),
|
|
}));
|
|
});
|
|
|
|
afterEach(() => {
|
|
createDialogSpy.mockRestore();
|
|
});
|
|
|
|
it("should handle room export click", async () => {
|
|
const { result } = render();
|
|
|
|
await act(async () => {
|
|
await result.current.onRoomExportClick();
|
|
});
|
|
expect(createDialogSpy).toHaveBeenCalledWith(ExportDialog, { room });
|
|
});
|
|
|
|
it("should handle room poll history click", async () => {
|
|
const { result } = render();
|
|
|
|
await act(async () => {
|
|
await result.current.onRoomPollHistoryClick();
|
|
});
|
|
expect(createDialogSpy).toHaveBeenCalledWith(PollHistoryDialog, {
|
|
room,
|
|
matrixClient,
|
|
permalinkCreator,
|
|
});
|
|
});
|
|
|
|
it("should handle room report click", async () => {
|
|
const { result } = render();
|
|
|
|
await act(async () => {
|
|
await result.current.onReportRoomClick();
|
|
});
|
|
|
|
expect(createDialogSpy).toHaveBeenCalledWith(ReportRoomDialog, { roomId: room.roomId });
|
|
});
|
|
|
|
it("should handle share room click", async () => {
|
|
const { result } = render();
|
|
|
|
await act(async () => {
|
|
await result.current.onShareRoomClick();
|
|
});
|
|
|
|
expect(createDialogSpy).toHaveBeenCalledWith(ShareDialog, {
|
|
target: room,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("favorite room state", () => {
|
|
it("should identify favorite rooms", () => {
|
|
jest.spyOn(getTagsForRoomUtils, "getTagsForRoom").mockReturnValue([DefaultTagID.Favourite]);
|
|
const { result } = render();
|
|
|
|
expect(result.current.isFavorite).toBe(true);
|
|
});
|
|
|
|
it("should identify non-favorite rooms", () => {
|
|
jest.spyOn(getTagsForRoomUtils, "getTagsForRoom").mockReturnValue([]);
|
|
const { result } = render();
|
|
|
|
expect(result.current.isFavorite).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("direct message state", () => {
|
|
it("should identify direct message rooms", async () => {
|
|
// Mock the direct rooms account data
|
|
const directRoomsList = {
|
|
"@user:domain.com": [room.roomId],
|
|
};
|
|
// Mock the useAccountData hook result
|
|
jest.spyOn(hooks, "useAccountData").mockReturnValue(directRoomsList);
|
|
|
|
const { result } = render();
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.isDirectMessage).toBe(true);
|
|
});
|
|
});
|
|
|
|
it("should identify non-direct message rooms", async () => {
|
|
// Mock the direct rooms account data
|
|
const directRoomsList = {};
|
|
// Mock the useAccountData hook result
|
|
jest.spyOn(hooks, "useAccountData").mockReturnValue(directRoomsList);
|
|
|
|
const { result } = render();
|
|
|
|
await waitFor(() => {
|
|
expect(result.current.isDirectMessage).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("search input", () => {
|
|
it("should handle search input escape key", () => {
|
|
const directRoomsList = {};
|
|
jest.spyOn(hooks, "useAccountData").mockReturnValue(directRoomsList);
|
|
const { result } = render();
|
|
// Create a mock input element and set it as the current ref value
|
|
const mockInputElement = document.createElement("input");
|
|
mockInputElement.value = "some search text";
|
|
|
|
result.current.searchInputRef.current = mockInputElement;
|
|
|
|
const event = {
|
|
key: "Escape",
|
|
preventDefault: jest.fn(),
|
|
stopPropagation: jest.fn(),
|
|
};
|
|
|
|
result.current.onUpdateSearchInput(event as any);
|
|
|
|
expect(onSearchCancel).toHaveBeenCalled();
|
|
expect(mockInputElement?.value).toBe("");
|
|
});
|
|
});
|
|
});
|