Mvvm RoomSummaryCard (#29674)

* feat: create roomsummarycard viewmodel

* feat: use roomsummurycard vm in component

* test: jest unit RoomSummaryCard and RoomSummaryCardViewModel

* chore: rename to roomsummarycardview

* feat: reput room topic without vm

* test: roomSummaryCard and roomSummaryCardVM tests

* chore: add comments on roomsummarycardVM

* fix: merge conflict with roomsummarytopic, and move to vm right_panel

* fix(roomsummarycard): remove usetransition for search update

* fix: merged file that should be deleted

* fix: roomsummurycard not well merge with roomtopic

* test: update snapshots
This commit is contained in:
Marc
2025-05-15 16:17:21 +02:00
committed by GitHub
parent 9642af9930
commit b07225eb60
7 changed files with 1108 additions and 570 deletions

View File

@@ -0,0 +1,265 @@
/*
* 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 RoomListStore from "../../../../../src/stores/room-list/RoomListStore";
import { DefaultTagID } from "../../../../../src/stores/room-list/models";
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";
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(RoomListStore.instance, "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(RoomListStore.instance, "getTagsForRoom").mockReturnValue([DefaultTagID.Favourite]);
const { result } = render();
expect(result.current.isFavorite).toBe(true);
});
it("should identify non-favorite rooms", () => {
jest.spyOn(RoomListStore.instance, "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("");
});
});
});

View File

@@ -1,394 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
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 React from "react";
import { render, fireEvent, screen, waitFor } from "jest-matrix-react";
import { EventType, MatrixEvent, Room, type MatrixClient, JoinRule } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { mocked, type MockedObject } from "jest-mock";
import userEvent from "@testing-library/user-event";
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
import RoomSummaryCard from "../../../../../src/components/views/right_panel/RoomSummaryCard";
import { ShareDialog } from "../../../../../src/components/views/dialogs/ShareDialog";
import ExportDialog from "../../../../../src/components/views/dialogs/ExportDialog";
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
import * as settingsHooks from "../../../../../src/hooks/useSettings";
import Modal from "../../../../../src/Modal";
import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelStore";
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases";
import { flushPromises, stubClient, untilDispatch } from "../../../../test-utils";
import { PollHistoryDialog } from "../../../../../src/components/views/dialogs/PollHistoryDialog";
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
import { _t } from "../../../../../src/languageHandler";
import { tagRoom } from "../../../../../src/utils/room/tagRoom";
import { DefaultTagID } from "../../../../../src/stores/room-list/models";
import { Action } from "../../../../../src/dispatcher/actions";
import { ReportRoomDialog } from "../../../../../src/components/views/dialogs/ReportRoomDialog.tsx";
import SettingsStore from "../../../../../src/settings/SettingsStore.ts";
import { SettingLevel } from "../../../../../src/settings/SettingLevel.ts";
jest.mock("../../../../../src/utils/room/tagRoom");
describe("<RoomSummaryCard />", () => {
const userId = "@alice:domain.org";
const roomId = "!room:domain.org";
let mockClient!: MockedObject<MatrixClient>;
let room!: Room;
const getComponent = (props = {}) => {
const defaultProps = {
room,
onClose: jest.fn(),
permalinkCreator: new RoomPermalinkCreator(room),
};
return render(<RoomSummaryCard {...defaultProps} {...props} />, {
wrapper: ({ children }) => (
<MatrixClientContext.Provider value={mockClient}>{children}</MatrixClientContext.Provider>
),
});
};
beforeEach(() => {
mockClient = mocked(stubClient());
room = new Room(roomId, mockClient, userId);
const roomCreateEvent = new MatrixEvent({
type: "m.room.create",
room_id: roomId,
sender: userId,
content: {
creator: userId,
room_version: "5",
},
state_key: "",
});
room.currentState.setStateEvents([roomCreateEvent]);
room.updateMyMembership(KnownMembership.Join);
jest.spyOn(Modal, "createDialog");
jest.spyOn(RightPanelStore.instance, "pushCard");
jest.spyOn(settingsHooks, "useFeatureEnabled").mockReturnValue(false);
jest.spyOn(defaultDispatcher, "dispatch");
jest.clearAllMocks();
DMRoomMap.makeShared(mockClient);
SettingsStore.setValue("releaseAnnouncementData", null, SettingLevel.DEVICE, { pinningMessageList: true });
mockClient.getRoom.mockReturnValue(room);
jest.spyOn(room, "isElementVideoRoom").mockRestore();
jest.spyOn(room, "isCallRoom").mockRestore();
});
afterEach(() => {
jest.restoreAllMocks();
});
it("renders the room summary", () => {
const { container } = getComponent();
expect(container).toMatchSnapshot();
});
it("renders the room topic in the summary", () => {
room.currentState.setStateEvents([
new MatrixEvent({
type: "m.room.topic",
room_id: roomId,
sender: userId,
content: {
topic: "This is the room's topic.",
},
state_key: "",
}),
]);
const { container } = getComponent();
expect(container).toMatchSnapshot();
});
it("has button to edit topic", () => {
room.currentState.setStateEvents([
new MatrixEvent({
type: "m.room.topic",
room_id: roomId,
sender: userId,
content: {
topic: "This is the room's topic.",
},
state_key: "",
}),
]);
const { container, getByText } = getComponent();
expect(getByText("Edit")).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
describe("search", () => {
it("has the search field", async () => {
const onSearchChange = jest.fn();
const { getByPlaceholderText } = getComponent({
onSearchChange,
});
expect(getByPlaceholderText("Search messages…")).toBeVisible();
});
it("should focus the search field if Action.FocusMessageSearch is fired", async () => {
const onSearchChange = jest.fn();
const { getByPlaceholderText } = getComponent({
onSearchChange,
});
expect(getByPlaceholderText("Search messages…")).not.toHaveFocus();
defaultDispatcher.fire(Action.FocusMessageSearch);
await waitFor(() => {
expect(getByPlaceholderText("Search messages…")).toHaveFocus();
});
});
it("should focus the search field if focusRoomSearch=true", () => {
const onSearchChange = jest.fn();
const { getByPlaceholderText } = getComponent({
onSearchChange,
focusRoomSearch: true,
});
expect(getByPlaceholderText("Search messages…")).toHaveFocus();
});
it("should cancel search on escape", () => {
const onSearchChange = jest.fn();
const onSearchCancel = jest.fn();
const { getByPlaceholderText } = getComponent({
onSearchChange,
onSearchCancel,
focusRoomSearch: true,
});
expect(getByPlaceholderText("Search messages…")).toHaveFocus();
fireEvent.keyDown(getByPlaceholderText("Search messages…"), { key: "Escape" });
expect(onSearchCancel).toHaveBeenCalled();
});
it("should update the search field value correctly", async () => {
const user = userEvent.setup();
const onSearchChange = jest.fn();
const { getByPlaceholderText } = getComponent({
onSearchChange,
});
const searchInput = getByPlaceholderText("Search messages…");
await user.type(searchInput, "test query");
expect(onSearchChange).toHaveBeenCalledWith("test query");
expect(searchInput).toHaveValue("test query");
});
});
it("opens room file panel on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText("Files"));
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith({ phase: RightPanelPhases.FilePanel }, true);
});
it("opens room export dialog on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("export_chat|title")));
expect(Modal.createDialog).toHaveBeenCalledWith(ExportDialog, { room });
});
it("opens share room dialog on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("action|copy_link")));
expect(Modal.createDialog).toHaveBeenCalledWith(ShareDialog, { target: room });
});
it("opens invite dialog on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("action|invite")));
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "view_invite", roomId: room.roomId });
});
it("fires favourite dispatch on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("room|context_menu|favourite")));
expect(tagRoom).toHaveBeenCalledWith(room, DefaultTagID.Favourite);
});
it("opens room settings on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("common|settings")));
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "open_room_settings" });
});
it("opens room member list on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText("People"));
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith({ phase: RightPanelPhases.MemberList }, true);
});
it("opens room threads list on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText("Threads"));
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith({ phase: RightPanelPhases.ThreadPanel }, true);
});
it("opens room pinned messages on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText("Pinned messages"));
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith(
{ phase: RightPanelPhases.PinnedMessages },
true,
);
});
it("dispatches leave room on button click", async () => {
jest.spyOn(Modal, "createDialog").mockReturnValueOnce({
finished: Promise.resolve([true]),
close: () => {},
});
const { getByText } = getComponent();
fireEvent.click(getByText(_t("room_list|more_options|leave_room")));
await untilDispatch("leave_room", defaultDispatcher);
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
action: "leave_room",
room_id: room.roomId,
});
});
it("opens report dialog on button click", async () => {
jest.spyOn(Modal, "createDialog").mockReturnValueOnce({
finished: Promise.resolve([true]),
close: () => {},
});
const { getByText } = getComponent();
fireEvent.click(getByText(_t("action|report_room")));
expect(Modal.createDialog).toHaveBeenCalledWith(ReportRoomDialog, { roomId: room.roomId });
await untilDispatch("leave_room", defaultDispatcher);
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
action: "leave_room",
room_id: room.roomId,
});
});
describe("pinning", () => {
it("renders pins options", () => {
const { getByText } = getComponent();
expect(getByText("Pinned messages")).toBeInTheDocument();
});
});
describe("poll history", () => {
it("renders poll history option", () => {
const { getByText } = getComponent();
expect(getByText("Polls")).toBeInTheDocument();
});
it("opens poll history dialog on button click", () => {
const permalinkCreator = new RoomPermalinkCreator(room);
const { getByText } = getComponent({ permalinkCreator });
fireEvent.click(getByText("Polls"));
expect(Modal.createDialog).toHaveBeenCalledWith(PollHistoryDialog, {
room,
matrixClient: mockClient,
permalinkCreator: permalinkCreator,
});
});
});
describe("video rooms", () => {
it("does not render irrelevant options for element video room", () => {
jest.spyOn(room, "isElementVideoRoom").mockReturnValue(true);
mocked(settingsHooks.useFeatureEnabled).mockImplementation((feature) => feature === "feature_video_rooms");
const { queryByText } = getComponent();
// options not rendered
expect(queryByText("Files")).not.toBeInTheDocument();
expect(queryByText("Pinned")).not.toBeInTheDocument();
expect(queryByText("Export chat")).not.toBeInTheDocument();
});
it("does not render irrelevant options for element call room", () => {
jest.spyOn(room, "isCallRoom").mockReturnValue(true);
mocked(settingsHooks.useFeatureEnabled).mockImplementation(
(feature) => feature === "feature_element_call_video_rooms" || feature === "feature_video_rooms",
);
const { queryByText } = getComponent();
// options not rendered
expect(queryByText("Files")).not.toBeInTheDocument();
expect(queryByText("Pinned")).not.toBeInTheDocument();
expect(queryByText("Export chat")).not.toBeInTheDocument();
});
});
describe("public room label", () => {
beforeEach(() => {
jest.spyOn(room.currentState, "getJoinRule").mockReturnValue(JoinRule.Public);
});
it("does not show public room label for a DM", async () => {
mockClient.getAccountData.mockImplementation((eventType) => {
if (eventType === EventType.Direct) {
return new MatrixEvent({
type: EventType.Direct,
content: {
"@bob:sesame.st": ["some-room-id"],
// this room is a DM with ernie
"@ernie:sesame.st": ["some-other-room-id", room.roomId],
},
});
}
});
getComponent();
await flushPromises();
expect(screen.queryByText("Public room")).not.toBeInTheDocument();
});
it("does not show public room label for non public room", async () => {
jest.spyOn(room.currentState, "getJoinRule").mockReturnValue(JoinRule.Invite);
getComponent();
await flushPromises();
expect(screen.queryByText("Public room")).not.toBeInTheDocument();
});
it("shows a public room label for a public room", async () => {
jest.spyOn(room.currentState, "getJoinRule").mockReturnValue(JoinRule.Public);
getComponent();
await flushPromises();
expect(screen.queryByText("Public room")).toBeInTheDocument();
});
});
});

View File

@@ -0,0 +1,324 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
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 React from "react";
import { render, fireEvent, screen } from "jest-matrix-react";
import { Room, type MatrixClient, JoinRule, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { mocked, type MockedObject } from "jest-mock";
import userEvent from "@testing-library/user-event";
import RoomSummaryCardView from "../../../../../src/components/views/right_panel/RoomSummaryCardView";
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
import { flushPromises, stubClient } from "../../../../test-utils";
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
import { _t } from "../../../../../src/languageHandler";
import {
type RoomSummaryCardState,
useRoomSummaryCardViewModel,
} from "../../../../../src/components/viewmodels/right_panel/RoomSummaryCardViewModel";
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
// Mock the viewmodel hooks
jest.mock("../../../../../src/components/viewmodels/right_panel/RoomSummaryCardViewModel", () => ({
useRoomSummaryCardViewModel: jest.fn(),
}));
describe("<RoomSummaryCard />", () => {
const userId = "@alice:domain.org";
const roomId = "!room:domain.org";
let mockClient!: MockedObject<MatrixClient>;
let room!: Room;
const getComponent = (props = {}) => {
const defaultProps = {
room,
onClose: jest.fn(),
permalinkCreator: new RoomPermalinkCreator(room),
};
return render(<RoomSummaryCardView {...defaultProps} {...props} />, {
wrapper: ({ children }) => (
<MatrixClientContext.Provider value={mockClient}>{children}</MatrixClientContext.Provider>
),
});
};
// Setup mock view models
const vmDefaultValues: RoomSummaryCardState = {
isDirectMessage: false,
isRoomEncrypted: false,
e2eStatus: undefined,
isVideoRoom: false,
roomJoinRule: JoinRule.Public,
alias: "",
isFavorite: false,
canInviteToState: true,
pinCount: 0,
searchInputRef: { current: null },
onUpdateSearchInput: jest.fn(),
onRoomMembersClick: jest.fn(),
onRoomThreadsClick: jest.fn(),
onRoomFilesClick: jest.fn(),
onRoomExtensionsClick: jest.fn(),
onRoomPinsClick: jest.fn(),
onRoomSettingsClick: jest.fn(),
onLeaveRoomClick: jest.fn(),
onShareRoomClick: jest.fn(),
onRoomExportClick: jest.fn(),
onRoomPollHistoryClick: jest.fn(),
onReportRoomClick: jest.fn(),
onFavoriteToggleClick: jest.fn(),
onInviteToRoomClick: jest.fn(),
};
beforeEach(() => {
mockClient = mocked(stubClient());
room = new Room(roomId, mockClient, userId);
mocked(useRoomSummaryCardViewModel).mockReturnValue(vmDefaultValues);
DMRoomMap.makeShared(mockClient);
mockClient.getRoom.mockReturnValue(room);
});
afterEach(() => {
jest.restoreAllMocks();
});
it("renders the room summary", () => {
const { container } = getComponent();
expect(container).toMatchSnapshot();
});
it("renders the room topic in the summary", () => {
room.currentState.setStateEvents([
new MatrixEvent({
type: "m.room.topic",
room_id: roomId,
sender: userId,
content: {
topic: "This is the room's topic.",
},
state_key: "",
}),
]);
const { container } = getComponent();
expect(container).toMatchSnapshot();
});
it("has button to edit topic", () => {
room.currentState.setStateEvents([
new MatrixEvent({
type: "m.room.topic",
room_id: roomId,
sender: userId,
content: {
topic: "This is the room's topic.",
},
state_key: "",
}),
]);
const { container, getByText } = getComponent();
expect(getByText("Edit")).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
describe("search", () => {
it("has the search field", async () => {
const onSearchChange = jest.fn();
const { getByPlaceholderText } = getComponent({
onSearchChange,
});
expect(getByPlaceholderText("Search messages…")).toBeVisible();
});
it("should focus the search field if focusRoomSearch=true", () => {
const onSearchChange = jest.fn();
const { getByPlaceholderText } = getComponent({
onSearchChange,
focusRoomSearch: true,
});
expect(getByPlaceholderText("Search messages…")).toHaveFocus();
});
it("should cancel search on escape", () => {
const onSearchChange = jest.fn();
const onSearchCancel = jest.fn();
const { getByPlaceholderText } = getComponent({
onSearchChange,
onSearchCancel,
focusRoomSearch: true,
});
expect(getByPlaceholderText("Search messages…")).toHaveFocus();
fireEvent.keyDown(getByPlaceholderText("Search messages…"), { key: "Escape" });
expect(vmDefaultValues.onUpdateSearchInput).toHaveBeenCalled();
});
it("should update the search field value correctly", async () => {
const user = userEvent.setup();
const onSearchChange = jest.fn();
const { getByPlaceholderText } = getComponent({
onSearchChange,
});
const searchInput = getByPlaceholderText("Search messages…");
await user.type(searchInput, "test query");
expect(onSearchChange).toHaveBeenCalledWith("test query");
expect(searchInput).toHaveValue("test query");
});
});
it("opens room file panel on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText("Files"));
expect(vmDefaultValues.onRoomFilesClick).toHaveBeenCalled();
});
it("opens room export dialog on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("export_chat|title")));
expect(vmDefaultValues.onRoomExportClick).toHaveBeenCalled();
});
it("opens share room dialog on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("action|copy_link")));
expect(vmDefaultValues.onShareRoomClick).toHaveBeenCalled();
});
it("opens invite dialog on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("action|invite")));
expect(vmDefaultValues.onInviteToRoomClick).toHaveBeenCalled();
});
it("fires favourite dispatch on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("room|context_menu|favourite")));
expect(vmDefaultValues.onFavoriteToggleClick).toHaveBeenCalled();
});
it("opens room settings on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText(_t("common|settings")));
expect(vmDefaultValues.onRoomSettingsClick).toHaveBeenCalled();
});
it("opens room member list on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText("People"));
expect(vmDefaultValues.onRoomMembersClick).toHaveBeenCalled();
});
it("opens room threads list on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText("Threads"));
expect(vmDefaultValues.onRoomThreadsClick).toHaveBeenCalled();
});
it("opens room pinned messages on button click", () => {
const { getByText } = getComponent();
fireEvent.click(getByText("Pinned messages"));
expect(vmDefaultValues.onRoomPinsClick).toHaveBeenCalled();
});
it("does not render irrelevant options if video room", () => {
mocked(useRoomSummaryCardViewModel).mockReturnValue({
...vmDefaultValues,
isVideoRoom: true,
});
const { queryByText } = getComponent();
// options not rendered
expect(queryByText("Files")).not.toBeInTheDocument();
expect(queryByText("Pinned")).not.toBeInTheDocument();
expect(queryByText("Export chat")).not.toBeInTheDocument();
});
describe("pinning", () => {
it("renders pins options", () => {
const { getByText } = getComponent();
expect(getByText("Pinned messages")).toBeInTheDocument();
});
});
describe("poll history", () => {
it("renders poll history option", () => {
const { getByText } = getComponent();
expect(getByText("Polls")).toBeInTheDocument();
});
it("opens poll history dialog on button click", () => {
const permalinkCreator = new RoomPermalinkCreator(room);
const { getByText } = getComponent({ permalinkCreator });
fireEvent.click(getByText("Polls"));
expect(vmDefaultValues.onRoomPollHistoryClick).toHaveBeenCalled();
});
});
describe("public room label", () => {
it("does not show public room label for a DM", async () => {
mocked(useRoomSummaryCardViewModel).mockReturnValue({
...vmDefaultValues,
isDirectMessage: true,
});
getComponent();
await flushPromises();
expect(screen.queryByText("Public room")).not.toBeInTheDocument();
});
it("does not show public room label for non public room", async () => {
mocked(useRoomSummaryCardViewModel).mockReturnValue({
...vmDefaultValues,
isDirectMessage: false,
roomJoinRule: JoinRule.Invite,
});
getComponent();
await flushPromises();
expect(screen.queryByText("Public room")).not.toBeInTheDocument();
});
it("shows a public room label for a public room", async () => {
getComponent();
await flushPromises();
expect(screen.queryByText("Public room")).toBeInTheDocument();
});
});
});

View File

@@ -10,12 +10,13 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
>
<div
class="mx_BaseCard_header"
data-floating-ui-inert=""
>
<div
class="mx_BaseCard_header_spacer"
/>
<button
aria-labelledby="«rm»"
aria-labelledby="«rq»"
class="_icon-button_m2erp_8 _subtle-bg_m2erp_29"
data-testid="base-card-close-button"
role="button"
@@ -46,6 +47,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
>
<header
class="mx_RoomSummaryCard_container"
data-floating-ui-inert=""
>
<span
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
@@ -71,6 +73,23 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
class="mx_Flex mx_RoomSummaryCard_badges"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
>
<span
class="_typography_6v6n8_153 _font-body-sm-medium_6v6n8_41 _badge_1t12g_8"
data-kind="grey"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 22a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m-1-2.05V18q-.825 0-1.412-.587A1.93 1.93 0 0 1 9 16v-1l-4.8-4.8q-.075.45-.138.9Q4 11.55 4 12q0 3.025 1.987 5.3T11 19.95m6.9-2.55q.5-.55.9-1.187.4-.638.662-1.326.263-.687.4-1.412Q20 12.75 20 12a7.85 7.85 0 0 0-1.363-4.475A7.7 7.7 0 0 0 15 4.6V5q0 .824-.588 1.412A1.93 1.93 0 0 1 13 7h-2v2q0 .424-.287.713A.97.97 0 0 1 10 10H8v2h6q.424 0 .713.287.287.288.287.713v3h1q.65 0 1.175.387.525.388.725 1.013"
/>
</svg>
Public room
</span>
<span
class="_typography_6v6n8_153 _font-body-sm-medium_6v6n8_41 _badge_1t12g_8"
data-kind="grey"
@@ -151,6 +170,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</header>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
@@ -162,6 +182,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
<div
aria-checked="false"
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitemcheckbox"
>
@@ -189,7 +210,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
<input
aria-hidden="true"
class="_input_19o42_24"
id="«rr»"
id="«rv»"
type="checkbox"
/>
<div
@@ -199,6 +220,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</div>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -236,12 +258,14 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</button>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -285,6 +309,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -324,8 +349,11 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</svg>
</button>
<div
aria-expanded="false"
aria-controls="«r12»"
aria-describedby="«r12»"
aria-expanded="true"
aria-haspopup="dialog"
data-floating-ui-inert=""
>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
@@ -372,8 +400,34 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</span>
</button>
</div>
<span
aria-hidden="true"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="-1"
/>
<span
data-floating-ui-focus-guard=""
data-type="outside"
role="button"
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="0"
/>
<span
aria-owns="«r14»"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
/>
<span
data-floating-ui-focus-guard=""
data-type="outside"
role="button"
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="0"
/>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -411,6 +465,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -448,12 +503,14 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</button>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -491,6 +548,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -528,6 +586,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -565,6 +624,7 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -605,12 +665,14 @@ exports[`<RoomSummaryCard /> has button to edit topic 1`] = `
</button>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<div
class="mx_RoomSummaryCard_bottomOptions"
data-floating-ui-inert=""
>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
@@ -706,6 +768,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
>
<div
class="mx_BaseCard_header"
data-floating-ui-inert=""
>
<div
class="mx_BaseCard_header_spacer"
@@ -742,6 +805,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
>
<header
class="mx_RoomSummaryCard_container"
data-floating-ui-inert=""
>
<span
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
@@ -767,6 +831,23 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
class="mx_Flex mx_RoomSummaryCard_badges"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
>
<span
class="_typography_6v6n8_153 _font-body-sm-medium_6v6n8_41 _badge_1t12g_8"
data-kind="grey"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 22a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m-1-2.05V18q-.825 0-1.412-.587A1.93 1.93 0 0 1 9 16v-1l-4.8-4.8q-.075.45-.138.9Q4 11.55 4 12q0 3.025 1.987 5.3T11 19.95m6.9-2.55q.5-.55.9-1.187.4-.638.662-1.326.263-.687.4-1.412Q20 12.75 20 12a7.85 7.85 0 0 0-1.363-4.475A7.7 7.7 0 0 0 15 4.6V5q0 .824-.588 1.412A1.93 1.93 0 0 1 13 7h-2v2q0 .424-.287.713A.97.97 0 0 1 10 10H8v2h6q.424 0 .713.287.287.288.287.713v3h1q.65 0 1.175.387.525.388.725 1.013"
/>
</svg>
Public room
</span>
<span
class="_typography_6v6n8_153 _font-body-sm-medium_6v6n8_41 _badge_1t12g_8"
data-kind="grey"
@@ -810,6 +891,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</header>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
@@ -821,6 +903,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
<div
aria-checked="false"
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitemcheckbox"
>
@@ -858,6 +941,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</div>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -895,12 +979,14 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</button>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -944,6 +1030,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -983,8 +1070,11 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</svg>
</button>
<div
aria-expanded="false"
aria-controls="«r8»"
aria-describedby="«r8»"
aria-expanded="true"
aria-haspopup="dialog"
data-floating-ui-inert=""
>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
@@ -1031,8 +1121,34 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</span>
</button>
</div>
<span
aria-hidden="true"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="-1"
/>
<span
data-floating-ui-focus-guard=""
data-type="outside"
role="button"
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="0"
/>
<span
aria-owns="«ra»"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
/>
<span
data-floating-ui-focus-guard=""
data-type="outside"
role="button"
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="0"
/>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1070,6 +1186,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1107,12 +1224,14 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</button>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1150,6 +1269,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1187,6 +1307,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1224,6 +1345,7 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1264,12 +1386,14 @@ exports[`<RoomSummaryCard /> renders the room summary 1`] = `
</button>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<div
class="mx_RoomSummaryCard_bottomOptions"
data-floating-ui-inert=""
>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
@@ -1365,12 +1489,13 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
>
<div
class="mx_BaseCard_header"
data-floating-ui-inert=""
>
<div
class="mx_BaseCard_header_spacer"
/>
<button
aria-labelledby="«rb»"
aria-labelledby="«rd»"
class="_icon-button_m2erp_8 _subtle-bg_m2erp_29"
data-testid="base-card-close-button"
role="button"
@@ -1401,6 +1526,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
>
<header
class="mx_RoomSummaryCard_container"
data-floating-ui-inert=""
>
<span
class="_avatar_1qbcf_8 mx_BaseAvatar _avatar-imageless_1qbcf_52"
@@ -1426,6 +1552,23 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
class="mx_Flex mx_RoomSummaryCard_badges"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
>
<span
class="_typography_6v6n8_153 _font-body-sm-medium_6v6n8_41 _badge_1t12g_8"
data-kind="grey"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 22a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m-1-2.05V18q-.825 0-1.412-.587A1.93 1.93 0 0 1 9 16v-1l-4.8-4.8q-.075.45-.138.9Q4 11.55 4 12q0 3.025 1.987 5.3T11 19.95m6.9-2.55q.5-.55.9-1.187.4-.638.662-1.326.263-.687.4-1.412Q20 12.75 20 12a7.85 7.85 0 0 0-1.363-4.475A7.7 7.7 0 0 0 15 4.6V5q0 .824-.588 1.412A1.93 1.93 0 0 1 13 7h-2v2q0 .424-.287.713A.97.97 0 0 1 10 10H8v2h6q.424 0 .713.287.287.288.287.713v3h1q.65 0 1.175.387.525.388.725 1.013"
/>
</svg>
Public room
</span>
<span
class="_typography_6v6n8_153 _font-body-sm-medium_6v6n8_41 _badge_1t12g_8"
data-kind="grey"
@@ -1506,6 +1649,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</header>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
@@ -1517,6 +1661,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
<div
aria-checked="false"
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitemcheckbox"
>
@@ -1544,7 +1689,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
<input
aria-hidden="true"
class="_input_19o42_24"
id="«rg»"
id="«ri»"
type="checkbox"
/>
<div
@@ -1554,6 +1699,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</div>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1591,12 +1737,14 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</button>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1640,6 +1788,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1679,8 +1828,11 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</svg>
</button>
<div
aria-expanded="false"
aria-controls="«rl»"
aria-describedby="«rl»"
aria-expanded="true"
aria-haspopup="dialog"
data-floating-ui-inert=""
>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
@@ -1727,8 +1879,34 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</span>
</button>
</div>
<span
aria-hidden="true"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="-1"
/>
<span
data-floating-ui-focus-guard=""
data-type="outside"
role="button"
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="0"
/>
<span
aria-owns="«rn»"
data-floating-ui-inert=""
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
/>
<span
data-floating-ui-focus-guard=""
data-type="outside"
role="button"
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
tabindex="0"
/>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1766,6 +1944,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1803,12 +1982,14 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</button>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1846,6 +2027,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1883,6 +2065,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1920,6 +2103,7 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</button>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"
data-floating-ui-inert=""
data-kind="primary"
role="menuitem"
>
@@ -1960,12 +2144,14 @@ exports[`<RoomSummaryCard /> renders the room topic in the summary 1`] = `
</button>
<div
class="_separator_7ckbw_8"
data-floating-ui-inert=""
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<div
class="mx_RoomSummaryCard_bottomOptions"
data-floating-ui-inert=""
>
<button
class="_item_dyt4i_8 _interactive_dyt4i_26"