Remove legacy room list (#34040)
* refactor(room-list): migrate SpaceStore off the legacy room list store SpaceStore.setActiveRoomInSpace iterated the legacy RoomListStore's `orderedLists` in `TAG_ORDER`; switch it to the space-aware RoomListStoreV3.getSortedRoomsInActiveSpace() accessor. This drops the last non-UI dependency on the legacy store and on `TAG_ORDER` (exported from LegacyRoomList, deleted next). * feat(room-list)!: remove the legacy room list UI Delete the old sublist-based room list and its components now that the new RoomListPanel is the default. Removed: LegacyRoomList, LegacyRoomListHeader, RoomSublist, ExtraTile, RoomTile (+ Subtitle/ CallSummary), RoomBreadcrumbs and RoomSearch, plus their styles and tests. LeftPanel collapses to the RoomListPanel-only path. The shared `contextMenuBelow` helper is relocated into RoomResultContextMenus (its only remaining consumer). * feat(room-list)!: remove the legacy RoomListStore The legacy sublist-based room list UI is gone, so the old `stores/room-list` store (Algorithm, sorters, filters, layout store, space watcher) has no remaining consumers. Delete the directory and its tests. MatrixChat.forgetRoom no longer calls the legacy `manualRoomUpdate`; the new room list store removes the room on the `AfterForgetRoom` dispatch that still fires. Drop the `mxRoomListStore`/`mxRoomListLayoutStore` globals and the now-dead test imports. * feat(room-list)!: remove the feature_new_room_list labs flag The new room list is now the only room list, so remove the feature_new_room_list labs flag and make its enabled behaviour unconditional everywhere it was gated: - LoggedInView: always use the resizable layout and NEW_ROOM_LIST_MIN_WIDTH; drop the collapsible/minimized legacy path. - SpaceStore: People and Favourites are dropped from metaSpaceOrder (per the long-standing TODO on the removed accessor). - MessagePreviewStore: stop appending thread replies to previews. - Settings, SidebarUserSettingsTab, PreferencesUserSettingsTab, QuickSettingsButton, SpacePanel, LandmarkNavigation: drop the flag reads and legacy branches. Update the tests that toggled the flag; the People/Favourites meta space tests covered behaviour that the flag (default on) already disabled. * feat(room-list)!: remove the dead legacy left-panel resizer LoggedInView still built the old `Resizer`/`CollapseDistributor` over an `lp-resizer` ResizeHandle and persisted `mx_lhs_size`. That handle is no longer rendered (the resizable layout is now driven by LeftResizablePanelView + ResizerViewModel, which persists its own state via RoomList.panelSize/RoomList.isPanelCollapsed), so the old resizer was inert dead code left over from the legacy room list. Remove createResizer/loadResizer/loadResizerPreferences, the _resizeContainer/resizeHandler refs, the ResizeHandle render, the mx_lhs_size handling and NEW_ROOM_LIST_MIN_WIDTH, plus the unit tests that exercised the mocked resizer. * feat(room-list)!: update i18n files * refactor(room-list): remove the now-unused collapseLhs state `collapseLhs` is write-only since the left panel no longer collapses: it was last read by LoggedInView's `shouldUseMinimizedUI`, removed with the feature_new_room_list flag. Drop it from MatrixChat's IState (and its assignments), collapsing the hide/show_left_panel handlers to just the `notifyLeftHandleResized()` call they still need, and from LoggedInView's IProps and the test props. * fix(room-list): instantiate message previewers lazily Removing the unused SettingsStore import from MessagePreviewStore (when the feature_new_room_list flag was dropped) changed module load order and exposed a latent circular dependency: ReactionEventPreview imports MessagePreviewStore, which eagerly did `new ReactionEventPreview()` at module-eval — so importing ReactionEventPreview first (as its unit test does) hit "ReactionEventPreview is not a constructor". Construct the previewers lazily on first use (cached) instead of at module load, so nothing dereferences a mid-evaluation module. Fixes ReactionEventPreview-test. * test(room-list): remove `feature_new_room_list` labs flag in e2e tests * chore: remove remaining `newRoomList` flag * chore: cleanup theme files * fix: restore the re-resizable TouchEvent polyfill * chore: remove usage of breadcrumbs settings in BreadcrumbStore * Revert "fix(room-list): instantiate message previewers lazily" This reverts commit 4e6eedfff0449c68a96c0470a4eb425b5aec5512. * chore: remove unused function in BreadCrumbStore * test: remove unused fuction of BreadcrumStore in tests * test: add tests for RoomResultContextMenu
This commit is contained in:
@@ -1,53 +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 { getByRole, render } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import React, { type ComponentProps } from "react";
|
||||
|
||||
import ExtraTile from "../../../../../src/components/views/rooms/ExtraTile";
|
||||
|
||||
describe("ExtraTile", () => {
|
||||
function renderComponent(props: Partial<ComponentProps<typeof ExtraTile>> = {}) {
|
||||
const defaultProps: ComponentProps<typeof ExtraTile> = {
|
||||
isMinimized: false,
|
||||
isSelected: false,
|
||||
displayName: "test",
|
||||
avatar: <React.Fragment />,
|
||||
onClick: () => {},
|
||||
};
|
||||
return render(<ExtraTile {...defaultProps} {...props} />);
|
||||
}
|
||||
|
||||
it("renders", () => {
|
||||
const { asFragment } = renderComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("hides text when minimized", () => {
|
||||
const { container } = renderComponent({
|
||||
isMinimized: true,
|
||||
displayName: "testDisplayName",
|
||||
});
|
||||
expect(container).not.toHaveTextContent("testDisplayName");
|
||||
});
|
||||
|
||||
it("registers clicks", async () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
const { container } = renderComponent({
|
||||
onClick,
|
||||
});
|
||||
|
||||
const btn = getByRole(container, "treeitem");
|
||||
|
||||
await userEvent.click(btn);
|
||||
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 Mikhail Aheichyk
|
||||
Copyright 2023 Nordeck IT + Consulting GmbH.
|
||||
|
||||
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, { type JSX } from "react";
|
||||
import { cleanup, queryByRole, render, screen, within } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { mocked } from "jest-mock";
|
||||
import { type Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import LegacyRoomList from "../../../../../src/components/views/rooms/LegacyRoomList";
|
||||
import ResizeNotifier from "../../../../../src/utils/ResizeNotifier";
|
||||
import { MetaSpace } from "../../../../../src/stores/spaces";
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../../src/settings/UIFeature";
|
||||
import dis from "../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import * as testUtils from "../../../../test-utils";
|
||||
import { mkSpace, stubClient } from "../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import SpaceStore from "../../../../../src/stores/spaces/SpaceStore";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import RoomListStore from "../../../../../src/stores/room-list/RoomListStore";
|
||||
import { type ITagMap } from "../../../../../src/stores/room-list/algorithms/models";
|
||||
import { DefaultTagID } from "../../../../../src/stores/room-list-v3/skip-list/tag";
|
||||
|
||||
jest.mock("../../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/dispatcher/dispatcher");
|
||||
|
||||
const getUserIdForRoomId = jest.fn();
|
||||
const getDMRoomsForUserId = jest.fn();
|
||||
// @ts-ignore
|
||||
DMRoomMap.sharedInstance = { getUserIdForRoomId, getDMRoomsForUserId };
|
||||
|
||||
describe("LegacyRoomList", () => {
|
||||
stubClient();
|
||||
const client = MatrixClientPeg.safeGet();
|
||||
const store = SpaceStore.instance;
|
||||
|
||||
function getComponent(props: Partial<LegacyRoomList["props"]> = {}): JSX.Element {
|
||||
return (
|
||||
<LegacyRoomList
|
||||
onKeyDown={jest.fn()}
|
||||
onFocus={jest.fn()}
|
||||
onBlur={jest.fn()}
|
||||
onResize={jest.fn()}
|
||||
resizeNotifier={new ResizeNotifier()}
|
||||
isMinimized={false}
|
||||
activeSpace={MetaSpace.Home}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
describe("Rooms", () => {
|
||||
describe("when meta space is active", () => {
|
||||
beforeEach(() => {
|
||||
store.setActiveSpace(MetaSpace.Home);
|
||||
});
|
||||
|
||||
it("does not render add room button when UIComponent customisation disables CreateRooms and ExploreRooms", () => {
|
||||
const disabled: UIComponent[] = [UIComponent.CreateRooms, UIComponent.ExploreRooms];
|
||||
mocked(shouldShowComponent).mockImplementation((feature) => !disabled.includes(feature));
|
||||
render(getComponent());
|
||||
|
||||
const roomsList = screen.getByRole("group", { name: "Rooms" });
|
||||
expect(within(roomsList).queryByRole("button", { name: "Add room" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders add room button with menu when UIComponent customisation allows CreateRooms or ExploreRooms", async () => {
|
||||
let disabled: UIComponent[] = [];
|
||||
mocked(shouldShowComponent).mockImplementation((feature) => !disabled.includes(feature));
|
||||
const { rerender } = render(getComponent());
|
||||
|
||||
const roomsList = screen.getByRole("group", { name: "Rooms" });
|
||||
const addRoomButton = within(roomsList).getByRole("button", { name: "Add room" });
|
||||
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
|
||||
|
||||
await userEvent.click(addRoomButton);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
|
||||
expect(within(menu).getByRole("menuitem", { name: "New room" })).toBeInTheDocument();
|
||||
expect(within(menu).getByRole("menuitem", { name: "Explore public rooms" })).toBeInTheDocument();
|
||||
|
||||
disabled = [UIComponent.CreateRooms];
|
||||
rerender(getComponent());
|
||||
|
||||
expect(addRoomButton).toBeInTheDocument();
|
||||
expect(menu).toBeInTheDocument();
|
||||
expect(within(menu).queryByRole("menuitem", { name: "New room" })).not.toBeInTheDocument();
|
||||
expect(within(menu).getByRole("menuitem", { name: "Explore public rooms" })).toBeInTheDocument();
|
||||
|
||||
disabled = [UIComponent.ExploreRooms];
|
||||
rerender(getComponent());
|
||||
|
||||
expect(addRoomButton).toBeInTheDocument();
|
||||
expect(menu).toBeInTheDocument();
|
||||
expect(within(menu).getByRole("menuitem", { name: "New room" })).toBeInTheDocument();
|
||||
expect(within(menu).queryByRole("menuitem", { name: "Explore public rooms" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders add room button and clicks explore public rooms", async () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
render(getComponent());
|
||||
|
||||
const roomsList = screen.getByRole("group", { name: "Rooms" });
|
||||
await userEvent.click(within(roomsList).getByRole("button", { name: "Add room" }));
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
await userEvent.click(within(menu).getByRole("menuitem", { name: "Explore public rooms" }));
|
||||
|
||||
expect(dis.fire).toHaveBeenCalledWith(Action.ViewRoomDirectory);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when room space is active", () => {
|
||||
let rooms: Room[];
|
||||
const mkSpaceForRooms = (spaceId: string, children: string[] = []) =>
|
||||
mkSpace(client, spaceId, rooms, children);
|
||||
|
||||
const space1 = "!space1:server";
|
||||
|
||||
beforeEach(async () => {
|
||||
rooms = [];
|
||||
mkSpaceForRooms(space1);
|
||||
mocked(client).getRoom.mockImplementation(
|
||||
(roomId) => rooms.find((room) => room.roomId === roomId) || null,
|
||||
);
|
||||
await testUtils.setupAsyncStoreWithClient(store, client);
|
||||
|
||||
store.setActiveSpace(space1);
|
||||
});
|
||||
|
||||
it("does not render add room button when UIComponent customisation disables CreateRooms and ExploreRooms", () => {
|
||||
const disabled: UIComponent[] = [UIComponent.CreateRooms, UIComponent.ExploreRooms];
|
||||
mocked(shouldShowComponent).mockImplementation((feature) => !disabled.includes(feature));
|
||||
render(getComponent());
|
||||
|
||||
const roomsList = screen.getByRole("group", { name: "Rooms" });
|
||||
expect(within(roomsList).queryByRole("button", { name: "Add room" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders add room button with menu when UIComponent customisation allows CreateRooms or ExploreRooms", async () => {
|
||||
let disabled: UIComponent[] = [];
|
||||
mocked(shouldShowComponent).mockImplementation((feature) => !disabled.includes(feature));
|
||||
const { rerender } = render(getComponent());
|
||||
|
||||
const roomsList = screen.getByRole("group", { name: "Rooms" });
|
||||
const addRoomButton = within(roomsList).getByRole("button", { name: "Add room" });
|
||||
expect(screen.queryByRole("menu")).not.toBeInTheDocument();
|
||||
|
||||
await userEvent.click(addRoomButton);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
|
||||
expect(within(menu).getByRole("menuitem", { name: "Explore rooms" })).toBeInTheDocument();
|
||||
expect(within(menu).getByRole("menuitem", { name: "New room" })).toBeInTheDocument();
|
||||
expect(within(menu).getByRole("menuitem", { name: "Add existing room" })).toBeInTheDocument();
|
||||
|
||||
disabled = [UIComponent.CreateRooms];
|
||||
rerender(getComponent());
|
||||
|
||||
expect(addRoomButton).toBeInTheDocument();
|
||||
expect(menu).toBeInTheDocument();
|
||||
expect(within(menu).getByRole("menuitem", { name: "Explore rooms" })).toBeInTheDocument();
|
||||
expect(within(menu).queryByRole("menuitem", { name: "New room" })).not.toBeInTheDocument();
|
||||
expect(within(menu).queryByRole("menuitem", { name: "Add existing room" })).not.toBeInTheDocument();
|
||||
|
||||
disabled = [UIComponent.ExploreRooms];
|
||||
rerender(getComponent());
|
||||
|
||||
expect(addRoomButton).toBeInTheDocument();
|
||||
expect(menu).toBeInTheDocument();
|
||||
expect(within(menu).queryByRole("menuitem", { name: "Explore rooms" })).toBeInTheDocument();
|
||||
expect(within(menu).getByRole("menuitem", { name: "New room" })).toBeInTheDocument();
|
||||
expect(within(menu).getByRole("menuitem", { name: "Add existing room" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders add room button and clicks explore rooms", async () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
render(getComponent());
|
||||
|
||||
const roomsList = screen.getByRole("group", { name: "Rooms" });
|
||||
await userEvent.click(within(roomsList).getByRole("button", { name: "Add room" }));
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
await userEvent.click(within(menu).getByRole("menuitem", { name: "Explore rooms" }));
|
||||
|
||||
expect(dis.dispatch).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
room_id: space1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when video meta space is active", () => {
|
||||
const videoRoomPrivate = "!videoRoomPrivate_server";
|
||||
const videoRoomPublic = "!videoRoomPublic_server";
|
||||
const videoRoomKnock = "!videoRoomKnock_server";
|
||||
|
||||
beforeEach(async () => {
|
||||
cleanup();
|
||||
const rooms: Room[] = [];
|
||||
testUtils.mkRoom(client, videoRoomPrivate, rooms);
|
||||
testUtils.mkRoom(client, videoRoomPublic, rooms);
|
||||
testUtils.mkRoom(client, videoRoomKnock, rooms);
|
||||
|
||||
mocked(client).getRoom.mockImplementation(
|
||||
(roomId) => rooms.find((room) => room.roomId === roomId) || null,
|
||||
);
|
||||
mocked(client).getRooms.mockImplementation(() => rooms);
|
||||
|
||||
const videoRoomKnockRoom = client.getRoom(videoRoomKnock)!;
|
||||
const videoRoomPrivateRoom = client.getRoom(videoRoomPrivate)!;
|
||||
const videoRoomPublicRoom = client.getRoom(videoRoomPublic)!;
|
||||
|
||||
[videoRoomPrivateRoom, videoRoomPublicRoom, videoRoomKnockRoom].forEach((room) => {
|
||||
(room.isCallRoom as jest.Mock).mockReturnValue(true);
|
||||
});
|
||||
|
||||
const roomLists: ITagMap = {};
|
||||
roomLists[DefaultTagID.Conference] = [videoRoomKnockRoom, videoRoomPublicRoom];
|
||||
roomLists[DefaultTagID.Untagged] = [videoRoomPrivateRoom];
|
||||
jest.spyOn(RoomListStore.instance, "orderedLists", "get").mockReturnValue(roomLists);
|
||||
await testUtils.setupAsyncStoreWithClient(store, client);
|
||||
|
||||
store.setActiveSpace(MetaSpace.VideoRooms);
|
||||
});
|
||||
|
||||
it("renders Conferences and Room but no People section", () => {
|
||||
const renderResult = render(getComponent({ activeSpace: MetaSpace.VideoRooms }));
|
||||
const roomsEl = renderResult.getByRole("treeitem", { name: "Rooms" });
|
||||
const conferenceEl = renderResult.getByRole("treeitem", { name: "Conferences" });
|
||||
|
||||
const noInvites = screen.queryByRole("treeitem", { name: "Invites" });
|
||||
const noFavourites = screen.queryByRole("treeitem", { name: "Favourites" });
|
||||
const noPeople = screen.queryByRole("treeitem", { name: "People" });
|
||||
const noLowPriority = screen.queryByRole("treeitem", { name: "Low priority" });
|
||||
const noHistorical = screen.queryByRole("treeitem", { name: "Historical" });
|
||||
|
||||
expect(roomsEl).toBeVisible();
|
||||
expect(conferenceEl).toBeVisible();
|
||||
|
||||
expect(noInvites).toBeFalsy();
|
||||
expect(noFavourites).toBeFalsy();
|
||||
expect(noPeople).toBeFalsy();
|
||||
expect(noLowPriority).toBeFalsy();
|
||||
expect(noHistorical).toBeFalsy();
|
||||
});
|
||||
it("renders Public and Knock rooms in Conferences section", () => {
|
||||
const renderResult = render(getComponent({ activeSpace: MetaSpace.VideoRooms }));
|
||||
const conferenceList = renderResult.getByRole("group", { name: "Conferences" });
|
||||
expect(queryByRole(conferenceList, "treeitem", { name: videoRoomPublic })).toBeVisible();
|
||||
expect(queryByRole(conferenceList, "treeitem", { name: videoRoomKnock })).toBeVisible();
|
||||
expect(queryByRole(conferenceList, "treeitem", { name: videoRoomPrivate })).toBeFalsy();
|
||||
|
||||
const roomsList = renderResult.getByRole("group", { name: "Rooms" });
|
||||
expect(queryByRole(roomsList, "treeitem", { name: videoRoomPrivate })).toBeVisible();
|
||||
expect(queryByRole(roomsList, "treeitem", { name: videoRoomPublic })).toBeFalsy();
|
||||
expect(queryByRole(roomsList, "treeitem", { name: videoRoomKnock })).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 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 { type MatrixClient, type Room, EventType } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
import { act, render, screen, fireEvent, type RenderResult } from "jest-matrix-react";
|
||||
|
||||
import SpaceStore from "../../../../../src/stores/spaces/SpaceStore";
|
||||
import { MetaSpace } from "../../../../../src/stores/spaces";
|
||||
import _RoomListHeader from "../../../../../src/components/views/rooms/LegacyRoomListHeader";
|
||||
import * as testUtils from "../../../../test-utils";
|
||||
import { stubClient, mkSpace } from "../../../../test-utils";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../../src/settings/UIFeature";
|
||||
|
||||
const RoomListHeader = testUtils.wrapInMatrixClientContext(_RoomListHeader);
|
||||
|
||||
jest.mock("../../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
}));
|
||||
|
||||
const blockUIComponent = (component: UIComponent): void => {
|
||||
mocked(shouldShowComponent).mockImplementation((feature) => feature !== component);
|
||||
};
|
||||
|
||||
const setupSpace = (client: MatrixClient): Room => {
|
||||
const testSpace: Room = mkSpace(client, "!space:server");
|
||||
testSpace.name = "Test Space";
|
||||
client.getRoom = () => testSpace;
|
||||
return testSpace;
|
||||
};
|
||||
|
||||
const setupMainMenu = async (client: MatrixClient, testSpace: Room): Promise<RenderResult> => {
|
||||
await testUtils.setupAsyncStoreWithClient(SpaceStore.instance, client);
|
||||
act(() => {
|
||||
SpaceStore.instance.setActiveSpace(testSpace.roomId);
|
||||
});
|
||||
|
||||
const wrapper = render(<RoomListHeader />);
|
||||
|
||||
expect(wrapper.getByText("Test Space")).toBeInTheDocument();
|
||||
wrapper.getByLabelText("Test Space menu").click();
|
||||
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
const setupPlusMenu = async (client: MatrixClient, testSpace: Room): Promise<RenderResult> => {
|
||||
await testUtils.setupAsyncStoreWithClient(SpaceStore.instance, client);
|
||||
act(() => {
|
||||
SpaceStore.instance.setActiveSpace(testSpace.roomId);
|
||||
});
|
||||
|
||||
const wrapper = render(<RoomListHeader />);
|
||||
|
||||
expect(wrapper.getByText("Test Space")).toBeInTheDocument();
|
||||
act(() => {
|
||||
wrapper.getByLabelText("Add")?.click();
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
const checkIsDisabled = (menuItem: HTMLElement): void => {
|
||||
expect(menuItem).toHaveAttribute("disabled");
|
||||
expect(menuItem).toHaveAttribute("aria-disabled", "true");
|
||||
};
|
||||
|
||||
const checkMenuLabels = (items: NodeListOf<Element>, labelArray: Array<string>) => {
|
||||
expect(items).toHaveLength(labelArray.length);
|
||||
|
||||
const checkLabel = (item: Element, label: string) => {
|
||||
expect(item.querySelector(".mx_IconizedContextMenu_label")).toHaveTextContent(label);
|
||||
};
|
||||
|
||||
labelArray.forEach((label, index) => {
|
||||
console.log("index", index, "label", label);
|
||||
checkLabel(items[index], label);
|
||||
});
|
||||
};
|
||||
|
||||
describe("RoomListHeader", () => {
|
||||
let client: MatrixClient;
|
||||
|
||||
beforeEach(async () => {
|
||||
// This tests the header from the old room list/left panel, the new one is now enabled by default,
|
||||
// so needs to be explicitly disabled to test the old list here.
|
||||
await SettingsStore.setValue("feature_new_room_list", null, SettingLevel.DEVICE, false);
|
||||
jest.resetAllMocks();
|
||||
|
||||
const dmRoomMap = {
|
||||
getUserIdForRoomId: jest.fn(),
|
||||
getDMRoomsForUserId: jest.fn(),
|
||||
} as unknown as DMRoomMap;
|
||||
DMRoomMap.setShared(dmRoomMap);
|
||||
stubClient();
|
||||
client = MatrixClientPeg.safeGet();
|
||||
mocked(shouldShowComponent).mockReturnValue(true); // show all UIComponents
|
||||
});
|
||||
|
||||
it("renders a main menu for the home space", () => {
|
||||
act(() => {
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Home);
|
||||
});
|
||||
|
||||
const { container } = render(<RoomListHeader />);
|
||||
|
||||
expect(container).toHaveTextContent("Home");
|
||||
fireEvent.click(screen.getByLabelText("Home options"));
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
const items = menu.querySelectorAll(".mx_IconizedContextMenu_item");
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]).toHaveTextContent("Show all rooms");
|
||||
});
|
||||
|
||||
it("renders a main menu for spaces", async () => {
|
||||
const testSpace = setupSpace(client);
|
||||
await setupMainMenu(client, testSpace);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
const items = menu.querySelectorAll(".mx_IconizedContextMenu_item");
|
||||
|
||||
checkMenuLabels(items, ["Space home", "Manage & explore rooms", "Preferences", "Settings", "Room", "Space"]);
|
||||
});
|
||||
|
||||
it("renders a plus menu for spaces", async () => {
|
||||
const testSpace = setupSpace(client);
|
||||
await setupPlusMenu(client, testSpace);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
const items = menu.querySelectorAll(".mx_IconizedContextMenu_item");
|
||||
|
||||
checkMenuLabels(items, ["New room", "Explore rooms", "Add existing room", "Add space"]);
|
||||
});
|
||||
|
||||
it("closes menu if space changes from under it", async () => {
|
||||
await SettingsStore.setValue("Spaces.enabledMetaSpaces", null, SettingLevel.DEVICE, {
|
||||
[MetaSpace.Home]: true,
|
||||
[MetaSpace.Favourites]: true,
|
||||
});
|
||||
|
||||
const testSpace = setupSpace(client);
|
||||
await setupMainMenu(client, testSpace);
|
||||
|
||||
act(() => {
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Favourites);
|
||||
});
|
||||
|
||||
screen.getByText("Favourites");
|
||||
expect(screen.queryByRole("menu")).toBeFalsy();
|
||||
});
|
||||
|
||||
describe("UIComponents", () => {
|
||||
describe("Main menu", () => {
|
||||
it("does not render Add Space when user does not have permission to add spaces", async () => {
|
||||
// User does not have permission to add spaces, anywhere
|
||||
blockUIComponent(UIComponent.CreateSpaces);
|
||||
|
||||
const testSpace = setupSpace(client);
|
||||
await setupMainMenu(client, testSpace);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
const items = menu.querySelectorAll(".mx_IconizedContextMenu_item");
|
||||
checkMenuLabels(items, [
|
||||
"Space home",
|
||||
"Manage & explore rooms",
|
||||
"Preferences",
|
||||
"Settings",
|
||||
"Room",
|
||||
// no add space
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not render Add Room when user does not have permission to add rooms", async () => {
|
||||
// User does not have permission to add rooms
|
||||
blockUIComponent(UIComponent.CreateRooms);
|
||||
|
||||
const testSpace = setupSpace(client);
|
||||
await setupMainMenu(client, testSpace);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
const items = menu.querySelectorAll(".mx_IconizedContextMenu_item");
|
||||
checkMenuLabels(items, [
|
||||
"Space home",
|
||||
"Explore rooms", // not Manage & explore rooms
|
||||
"Preferences",
|
||||
"Settings",
|
||||
// no add room
|
||||
"Space",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Plus menu", () => {
|
||||
it("does not render Add Space when user does not have permission to add spaces", async () => {
|
||||
// User does not have permission to add spaces, anywhere
|
||||
blockUIComponent(UIComponent.CreateSpaces);
|
||||
|
||||
const testSpace = setupSpace(client);
|
||||
await setupPlusMenu(client, testSpace);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
const items = menu.querySelectorAll(".mx_IconizedContextMenu_item");
|
||||
|
||||
checkMenuLabels(items, [
|
||||
"New room",
|
||||
"Explore rooms",
|
||||
"Add existing room",
|
||||
// no Add space
|
||||
]);
|
||||
});
|
||||
|
||||
it("disables Add Room when user does not have permission to add rooms", async () => {
|
||||
// User does not have permission to add rooms
|
||||
blockUIComponent(UIComponent.CreateRooms);
|
||||
|
||||
const testSpace = setupSpace(client);
|
||||
await setupPlusMenu(client, testSpace);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
const items = menu.querySelectorAll<HTMLElement>(".mx_IconizedContextMenu_item");
|
||||
|
||||
checkMenuLabels(items, ["New room", "Explore rooms", "Add existing room", "Add space"]);
|
||||
|
||||
// "Add existing room" is disabled
|
||||
checkIsDisabled(items[2]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("adding children to space", () => {
|
||||
it("if user cannot add children to space, MainMenu adding buttons are hidden", async () => {
|
||||
const testSpace = setupSpace(client);
|
||||
mocked(testSpace.currentState.maySendStateEvent).mockImplementation(
|
||||
(stateEventType, userId) => stateEventType !== EventType.SpaceChild,
|
||||
);
|
||||
|
||||
await setupMainMenu(client, testSpace);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
const items = menu.querySelectorAll(".mx_IconizedContextMenu_item");
|
||||
checkMenuLabels(items, [
|
||||
"Space home",
|
||||
"Explore rooms", // not Manage & explore rooms
|
||||
"Preferences",
|
||||
"Settings",
|
||||
// no add room
|
||||
// no add space
|
||||
]);
|
||||
});
|
||||
|
||||
it("if user cannot add children to space, PlusMenu add buttons are disabled", async () => {
|
||||
const testSpace = setupSpace(client);
|
||||
mocked(testSpace.currentState.maySendStateEvent).mockImplementation(
|
||||
(stateEventType, userId) => stateEventType !== EventType.SpaceChild,
|
||||
);
|
||||
|
||||
await setupPlusMenu(client, testSpace);
|
||||
|
||||
const menu = screen.getByRole("menu");
|
||||
const items = menu.querySelectorAll<HTMLElement>(".mx_IconizedContextMenu_item");
|
||||
|
||||
checkMenuLabels(items, ["New room", "Explore rooms", "Add existing room", "Add space"]);
|
||||
|
||||
// "Add existing room" is disabled
|
||||
checkIsDisabled(items[2]);
|
||||
// "Add space" is disabled
|
||||
checkIsDisabled(items[3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,317 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 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, screen, act, type RenderResult } from "jest-matrix-react";
|
||||
import { mocked, type Mocked } from "jest-mock";
|
||||
import {
|
||||
type MatrixClient,
|
||||
PendingEventOrdering,
|
||||
Room,
|
||||
RoomStateEvent,
|
||||
type Thread,
|
||||
type RoomMember,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import { Widget } from "matrix-widget-api";
|
||||
|
||||
import {
|
||||
stubClient,
|
||||
mkRoomMember,
|
||||
MockedCall,
|
||||
useMockedCalls,
|
||||
setupAsyncStoreWithClient,
|
||||
filterConsole,
|
||||
flushPromises,
|
||||
mkMessage,
|
||||
useMockMediaDevices,
|
||||
} from "../../../../test-utils";
|
||||
import { CallStore } from "../../../../../src/stores/CallStore";
|
||||
import RoomTile from "../../../../../src/components/views/rooms/RoomTile";
|
||||
import { DefaultTagID } from "../../../../../src/stores/room-list-v3/skip-list/tag";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import PlatformPeg from "../../../../../src/PlatformPeg";
|
||||
import type BasePlatform from "../../../../../src/BasePlatform";
|
||||
import { WidgetMessagingStore } from "../../../../../src/stores/widgets/WidgetMessagingStore";
|
||||
import { TestSDKContext } from "../../../TestSDKContext";
|
||||
import { SDKContext } from "../../../../../src/contexts/SDKContext";
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../../src/settings/UIFeature";
|
||||
import { MessagePreviewStore } from "../../../../../src/stores/message-preview";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { ConnectionState } from "../../../../../src/models/Call";
|
||||
import { type WidgetMessaging } from "../../../../../src/stores/widgets/WidgetMessaging";
|
||||
|
||||
jest.mock("../../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("RoomTile", () => {
|
||||
jest.spyOn(PlatformPeg, "get").mockReturnValue({
|
||||
overrideBrowserShortcuts: () => false,
|
||||
} as unknown as BasePlatform);
|
||||
useMockedCalls();
|
||||
|
||||
const renderRoomTile = (): RenderResult => {
|
||||
return render(
|
||||
<SDKContext.Provider value={sdkContext}>
|
||||
<RoomTile
|
||||
room={room}
|
||||
showMessagePreview={showMessagePreview}
|
||||
isMinimized={false}
|
||||
tag={DefaultTagID.Untagged}
|
||||
/>
|
||||
</SDKContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
let client: Mocked<MatrixClient>;
|
||||
let room: Room;
|
||||
let sdkContext: TestSDKContext;
|
||||
let showMessagePreview = false;
|
||||
|
||||
filterConsole(
|
||||
// irrelevant for this test
|
||||
"Room !1:example.org does not have an m.room.create event",
|
||||
);
|
||||
|
||||
const addMessageToRoom = (ts: number) => {
|
||||
const message = mkMessage({
|
||||
event: true,
|
||||
room: room.roomId,
|
||||
msg: "test message",
|
||||
user: client.getSafeUserId(),
|
||||
ts,
|
||||
});
|
||||
|
||||
room.timeline.push(message);
|
||||
};
|
||||
|
||||
const addThreadMessageToRoom = (ts: number) => {
|
||||
const message = mkMessage({
|
||||
event: true,
|
||||
room: room.roomId,
|
||||
msg: "test thread reply",
|
||||
user: client.getSafeUserId(),
|
||||
ts,
|
||||
});
|
||||
|
||||
// Mock thread reply for tests.
|
||||
jest.spyOn(room, "getThreads").mockReturnValue([
|
||||
// @ts-ignore
|
||||
{
|
||||
lastReply: () => message,
|
||||
timeline: [],
|
||||
} as Thread,
|
||||
]);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
useMockMediaDevices();
|
||||
sdkContext = new TestSDKContext();
|
||||
|
||||
client = mocked(stubClient());
|
||||
sdkContext.client = client;
|
||||
DMRoomMap.makeShared(client);
|
||||
|
||||
room = new Room("!1:example.org", client, "@alice:example.org", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
|
||||
client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
|
||||
client.getRooms.mockReturnValue([room]);
|
||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// @ts-ignore
|
||||
MessagePreviewStore.instance.previews = new Map<string, Map<TagID | TAG_ANY, MessagePreview | null>>();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("when message previews are not enabled", () => {
|
||||
it("should render the room", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
const { container } = renderRoomTile();
|
||||
expect(container).toMatchSnapshot();
|
||||
expect(container.querySelector(".mx_RoomTile_sticky")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render the room options context menu when UIComponent customisations disable room options", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(false);
|
||||
renderRoomTile();
|
||||
expect(shouldShowComponent).toHaveBeenCalledWith(UIComponent.RoomOptionsMenu);
|
||||
expect(screen.queryByRole("button", { name: "Room options" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the room options context menu when UIComponent customisations enable room options", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
renderRoomTile();
|
||||
expect(shouldShowComponent).toHaveBeenCalledWith(UIComponent.RoomOptionsMenu);
|
||||
expect(screen.queryByRole("button", { name: "Room options" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render the room options context menu when knocked to the room", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
|
||||
return name === "feature_ask_to_join";
|
||||
});
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Knock);
|
||||
const { container } = renderRoomTile();
|
||||
expect(container.querySelector(".mx_RoomTile_sticky")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Room options" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render the room options context menu when knock has been denied", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
|
||||
return name === "feature_ask_to_join";
|
||||
});
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
const roomMember = mkRoomMember(
|
||||
room.roomId,
|
||||
MatrixClientPeg.get()!.getSafeUserId(),
|
||||
KnownMembership.Leave,
|
||||
true,
|
||||
{
|
||||
membership: KnownMembership.Knock,
|
||||
},
|
||||
);
|
||||
jest.spyOn(room, "getMember").mockReturnValue(roomMember);
|
||||
const { container } = renderRoomTile();
|
||||
expect(container.querySelector(".mx_RoomTile_sticky")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Room options" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("when a call starts", () => {
|
||||
let call: MockedCall;
|
||||
let widget: Widget;
|
||||
|
||||
beforeEach(() => {
|
||||
setupAsyncStoreWithClient(CallStore.instance, client);
|
||||
setupAsyncStoreWithClient(WidgetMessagingStore.instance, client);
|
||||
|
||||
MockedCall.create(room, "1");
|
||||
const maybeCall = CallStore.instance.getCall(room.roomId);
|
||||
if (!(maybeCall instanceof MockedCall)) throw new Error("Failed to create call");
|
||||
call = maybeCall;
|
||||
|
||||
widget = new Widget(call.widget);
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, {
|
||||
stop: () => {},
|
||||
} as unknown as WidgetMessaging);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
call.destroy();
|
||||
client.reEmitter.stopReEmitting(room, [RoomStateEvent.Events]);
|
||||
WidgetMessagingStore.instance.stopMessaging(widget, room.roomId);
|
||||
});
|
||||
|
||||
it("tracks connection state", async () => {
|
||||
renderRoomTile();
|
||||
screen.getByText("Video");
|
||||
act(() => call.setConnectionState(ConnectionState.Connected));
|
||||
screen.getByText("Joined");
|
||||
await act(() => call.disconnect());
|
||||
screen.getByText("Video");
|
||||
});
|
||||
|
||||
it("tracks participants", () => {
|
||||
renderRoomTile();
|
||||
const alice: [RoomMember, Set<string>] = [
|
||||
mkRoomMember(room.roomId, "@alice:example.org"),
|
||||
new Set(["a"]),
|
||||
];
|
||||
const bob: [RoomMember, Set<string>] = [
|
||||
mkRoomMember(room.roomId, "@bob:example.org"),
|
||||
new Set(["b1", "b2"]),
|
||||
];
|
||||
const carol: [RoomMember, Set<string>] = [
|
||||
mkRoomMember(room.roomId, "@carol:example.org"),
|
||||
new Set(["c"]),
|
||||
];
|
||||
|
||||
expect(screen.queryByLabelText(/participant/)).toBe(null);
|
||||
|
||||
act(() => {
|
||||
call.participants = new Map([alice]);
|
||||
});
|
||||
expect(screen.getByLabelText("1 person joined").textContent).toBe("1");
|
||||
|
||||
act(() => {
|
||||
call.participants = new Map([alice, bob, carol]);
|
||||
});
|
||||
expect(screen.getByLabelText("4 people joined").textContent).toBe("4");
|
||||
|
||||
act(() => {
|
||||
call.participants = new Map();
|
||||
});
|
||||
expect(screen.queryByLabelText(/participant/)).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when message previews are enabled", () => {
|
||||
beforeEach(() => {
|
||||
showMessagePreview = true;
|
||||
});
|
||||
|
||||
it("should render a room without a message as expected", async () => {
|
||||
const renderResult = renderRoomTile();
|
||||
// flush promises here because the preview is created asynchronously
|
||||
await flushPromises();
|
||||
expect(renderResult.asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("and there is a message in the room", () => {
|
||||
beforeEach(() => {
|
||||
addMessageToRoom(23);
|
||||
});
|
||||
|
||||
it("should render as expected", async () => {
|
||||
const renderResult = renderRoomTile();
|
||||
expect(await screen.findByText("test message")).toBeInTheDocument();
|
||||
expect(renderResult.asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and there is a message in a thread", () => {
|
||||
beforeEach(() => {
|
||||
addThreadMessageToRoom(23);
|
||||
});
|
||||
|
||||
it("should render as expected", async () => {
|
||||
const renderResult = renderRoomTile();
|
||||
expect(await screen.findByText("test thread reply")).toBeInTheDocument();
|
||||
expect(renderResult.asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and there is a message and a thread without a reply", () => {
|
||||
beforeEach(() => {
|
||||
addMessageToRoom(23);
|
||||
|
||||
// Mock thread reply for tests.
|
||||
jest.spyOn(room, "getThreads").mockReturnValue([
|
||||
// @ts-ignore
|
||||
{
|
||||
lastReply: () => null,
|
||||
timeline: [],
|
||||
findEventById: () => {},
|
||||
} as Thread,
|
||||
]);
|
||||
});
|
||||
|
||||
it("should render the message preview", async () => {
|
||||
renderRoomTile();
|
||||
expect(await screen.findByText("test message")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`ExtraTile renders 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
aria-label="test"
|
||||
class="mx_AccessibleButton mx_ExtraTile mx_RoomTile"
|
||||
role="treeitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomTile_avatarContainer"
|
||||
/>
|
||||
<div
|
||||
class="mx_RoomTile_details"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomTile_primaryDetails"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomTile_titleContainer"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomTile_title"
|
||||
dir="auto"
|
||||
tabindex="-1"
|
||||
title="test"
|
||||
>
|
||||
test
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomTile_badgeContainer"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -1,378 +0,0 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`RoomTile when message previews are enabled and there is a message in a thread should render as expected 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
aria-describedby="mx_RoomTile_messagePreview_!1:example.org"
|
||||
aria-label="!1:example.org"
|
||||
aria-selected="false"
|
||||
class="mx_AccessibleButton mx_RoomTile"
|
||||
role="treeitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DecoratedRoomAvatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_va14e_8 mx_BaseAvatar _avatar-imageless_va14e_55"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomTile_titleContainer"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomTile_title mx_RoomTile_titleWithSubtitle"
|
||||
tabindex="-1"
|
||||
title="!1:example.org"
|
||||
>
|
||||
<span
|
||||
dir="auto"
|
||||
>
|
||||
!1:example.org
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomTile_subtitle"
|
||||
id="mx_RoomTile_messagePreview_!1:example.org"
|
||||
title="test thread reply"
|
||||
>
|
||||
<span
|
||||
class="mx_RoomTile_subtitle_text"
|
||||
>
|
||||
test thread reply
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="mx_RoomTile_badgeContainer"
|
||||
/>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Room options"
|
||||
class="mx_AccessibleButton mx_RoomTile_menuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Notification options"
|
||||
class="mx_AccessibleButton mx_RoomTile_notificationsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`RoomTile when message previews are enabled and there is a message in the room should render as expected 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
aria-describedby="mx_RoomTile_messagePreview_!1:example.org"
|
||||
aria-label="!1:example.org Unread messages."
|
||||
aria-selected="false"
|
||||
class="mx_AccessibleButton mx_RoomTile"
|
||||
role="treeitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DecoratedRoomAvatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_va14e_8 mx_BaseAvatar _avatar-imageless_va14e_55"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomTile_titleContainer"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomTile_title mx_RoomTile_titleWithSubtitle mx_RoomTile_titleHasUnreadEvents"
|
||||
tabindex="-1"
|
||||
title="!1:example.org"
|
||||
>
|
||||
<span
|
||||
dir="auto"
|
||||
>
|
||||
!1:example.org
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomTile_subtitle"
|
||||
id="mx_RoomTile_messagePreview_!1:example.org"
|
||||
title="test message"
|
||||
>
|
||||
<span
|
||||
class="mx_RoomTile_subtitle_text"
|
||||
>
|
||||
test message
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="mx_RoomTile_badgeContainer"
|
||||
>
|
||||
<div
|
||||
class="mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_dot"
|
||||
>
|
||||
<span
|
||||
class="mx_NotificationBadge_count"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Room options"
|
||||
class="mx_AccessibleButton mx_RoomTile_menuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Notification options"
|
||||
class="mx_AccessibleButton mx_RoomTile_notificationsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`RoomTile when message previews are enabled should render a room without a message as expected 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
aria-describedby="mx_RoomTile_messagePreview_!1:example.org"
|
||||
aria-label="!1:example.org"
|
||||
aria-selected="false"
|
||||
class="mx_AccessibleButton mx_RoomTile"
|
||||
role="treeitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DecoratedRoomAvatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_va14e_8 mx_BaseAvatar _avatar-imageless_va14e_55"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomTile_titleContainer"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomTile_title"
|
||||
tabindex="-1"
|
||||
title="!1:example.org"
|
||||
>
|
||||
<span
|
||||
dir="auto"
|
||||
>
|
||||
!1:example.org
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="mx_RoomTile_badgeContainer"
|
||||
/>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Room options"
|
||||
class="mx_AccessibleButton mx_RoomTile_menuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Notification options"
|
||||
class="mx_AccessibleButton mx_RoomTile_notificationsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`RoomTile when message previews are not enabled should render the room 1`] = `
|
||||
<div>
|
||||
<div
|
||||
aria-label="!1:example.org"
|
||||
aria-selected="false"
|
||||
class="mx_AccessibleButton mx_RoomTile"
|
||||
role="treeitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DecoratedRoomAvatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_va14e_8 mx_BaseAvatar _avatar-imageless_va14e_55"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
!
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RoomTile_titleContainer"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomTile_title"
|
||||
tabindex="-1"
|
||||
title="!1:example.org"
|
||||
>
|
||||
<span
|
||||
dir="auto"
|
||||
>
|
||||
!1:example.org
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="mx_RoomTile_badgeContainer"
|
||||
/>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Room options"
|
||||
class="mx_AccessibleButton mx_RoomTile_menuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Notification options"
|
||||
class="mx_AccessibleButton mx_RoomTile_notificationsButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M20.293 17.293c.63.63.184 1.707-.707 1.707H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-7 7-7 7 7 7 7v6zM12 22a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
Reference in New Issue
Block a user