First step to add header to new room list (#29320)
* feat: create new header * test: add tests to view model * test: add tests to view * feat: add header to new room list * test(e2e): update RoomListView snapshot * test(e2e): add tests for room list header * refactor: minor code improvement
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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 { renderHook } from "jest-matrix-react";
|
||||
import { type MatrixClient, RoomType } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { useRoomListHeaderViewModel } from "../../../../../src/components/viewmodels/roomlist/RoomListHeaderViewModel";
|
||||
import SpaceStore from "../../../../../src/stores/spaces/SpaceStore";
|
||||
import { mkStubRoom, stubClient } from "../../../../test-utils";
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
|
||||
jest.mock("../../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("useRoomListHeaderViewModel", () => {
|
||||
let matrixClient: MatrixClient;
|
||||
|
||||
beforeEach(() => {
|
||||
matrixClient = stubClient();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe("title", () => {
|
||||
it("should return Home as title", () => {
|
||||
const { result } = renderHook(() => useRoomListHeaderViewModel());
|
||||
expect(result.current.title).toStrictEqual("Home");
|
||||
});
|
||||
|
||||
it("should return the current space name as title", () => {
|
||||
const room = mkStubRoom("spaceId", "spaceName", matrixClient);
|
||||
jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(room);
|
||||
const { result } = renderHook(() => useRoomListHeaderViewModel());
|
||||
|
||||
expect(result.current.title).toStrictEqual("spaceName");
|
||||
});
|
||||
});
|
||||
|
||||
it("should be displayComposeMenu=true and canCreateRoom=true if the user can creates room", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(false);
|
||||
const { result, rerender } = renderHook(() => useRoomListHeaderViewModel());
|
||||
expect(result.current.displayComposeMenu).toBe(false);
|
||||
expect(result.current.canCreateRoom).toBe(false);
|
||||
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
rerender();
|
||||
expect(result.current.displayComposeMenu).toBe(true);
|
||||
expect(result.current.canCreateRoom).toBe(true);
|
||||
});
|
||||
|
||||
it("should be canCreateVideoRoom=true if feature_video_rooms is enabled", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
const { result } = renderHook(() => useRoomListHeaderViewModel());
|
||||
expect(result.current.canCreateVideoRoom).toBe(true);
|
||||
});
|
||||
|
||||
it("should fire Action.CreateChat when createChatRoom is called", () => {
|
||||
const spy = jest.spyOn(defaultDispatcher, "fire");
|
||||
const { result } = renderHook(() => useRoomListHeaderViewModel());
|
||||
result.current.createChatRoom(new Event("click"));
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(Action.CreateChat);
|
||||
});
|
||||
|
||||
it("should fire Action.CreateRoom when createRoom is called", () => {
|
||||
const spy = jest.spyOn(defaultDispatcher, "fire");
|
||||
const { result } = renderHook(() => useRoomListHeaderViewModel());
|
||||
result.current.createRoom(new Event("click"));
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(Action.CreateRoom);
|
||||
});
|
||||
|
||||
it("should fire Action.CreateRoom with RoomType.UnstableCall when createVideoRoom is called and feature_element_call_video_rooms is enabled", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
const spy = jest.spyOn(defaultDispatcher, "dispatch");
|
||||
const { result } = renderHook(() => useRoomListHeaderViewModel());
|
||||
result.current.createVideoRoom();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ action: Action.CreateRoom, type: RoomType.UnstableCall });
|
||||
});
|
||||
|
||||
it("should fire Action.CreateRoom with RoomType.ElementVideo when createVideoRoom is called and feature_element_call_video_rooms is disabled", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
const spy = jest.spyOn(defaultDispatcher, "dispatch");
|
||||
const { result } = renderHook(() => useRoomListHeaderViewModel());
|
||||
result.current.createVideoRoom();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ action: Action.CreateRoom, type: RoomType.ElementVideo });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import {
|
||||
type RoomListHeaderViewState,
|
||||
useRoomListHeaderViewModel,
|
||||
} from "../../../../../../src/components/viewmodels/roomlist/RoomListHeaderViewModel";
|
||||
import { RoomListHeaderView } from "../../../../../../src/components/views/rooms/RoomListView/RoomListHeaderView";
|
||||
|
||||
jest.mock("../../../../../../src/components/viewmodels/roomlist/RoomListHeaderViewModel", () => ({
|
||||
useRoomListHeaderViewModel: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("<RoomListHeaderView />", () => {
|
||||
const defaultValue: RoomListHeaderViewState = {
|
||||
title: "title",
|
||||
displayComposeMenu: true,
|
||||
canCreateRoom: true,
|
||||
canCreateVideoRoom: true,
|
||||
createRoom: jest.fn(),
|
||||
createVideoRoom: jest.fn(),
|
||||
createChatRoom: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should display the compose menu", () => {
|
||||
mocked(useRoomListHeaderViewModel).mockReturnValue(defaultValue);
|
||||
|
||||
const { asFragment } = render(<RoomListHeaderView />);
|
||||
expect(screen.queryByRole("button", { name: "Add" })).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should not display the compose menu", () => {
|
||||
mocked(useRoomListHeaderViewModel).mockReturnValue({ ...defaultValue, displayComposeMenu: false });
|
||||
|
||||
const { asFragment } = render(<RoomListHeaderView />);
|
||||
expect(screen.queryByRole("button", { name: "Add" })).toBeNull();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display all the buttons when the menu is opened", async () => {
|
||||
const user = userEvent.setup();
|
||||
mocked(useRoomListHeaderViewModel).mockReturnValue(defaultValue);
|
||||
render(<RoomListHeaderView />);
|
||||
const openMenu = screen.getByRole("button", { name: "Add" });
|
||||
await user.click(openMenu);
|
||||
|
||||
await user.click(screen.getByRole("menuitem", { name: "New message" }));
|
||||
expect(defaultValue.createChatRoom).toHaveBeenCalled();
|
||||
|
||||
await user.click(openMenu);
|
||||
await user.click(screen.getByRole("menuitem", { name: "New room" }));
|
||||
expect(defaultValue.createRoom).toHaveBeenCalled();
|
||||
|
||||
await user.click(openMenu);
|
||||
await user.click(screen.getByRole("menuitem", { name: "New video room" }));
|
||||
expect(defaultValue.createVideoRoom).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should display only the new message button", async () => {
|
||||
const user = userEvent.setup();
|
||||
mocked(useRoomListHeaderViewModel).mockReturnValue({
|
||||
...defaultValue,
|
||||
canCreateRoom: false,
|
||||
canCreateVideoRoom: false,
|
||||
});
|
||||
|
||||
render(<RoomListHeaderView />);
|
||||
await user.click(screen.getByRole("button", { name: "Add" }));
|
||||
|
||||
expect(screen.queryByRole("menuitem", { name: "New room" })).toBeNull();
|
||||
expect(screen.queryByRole("menuitem", { name: "New video room" })).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<RoomListHeaderView /> should display the compose menu 1`] = `
|
||||
<DocumentFragment>
|
||||
<header
|
||||
aria-label="Room options"
|
||||
class="mx_Flex mx_RoomListHeaderView"
|
||||
data-testid="room-list-header"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
|
||||
>
|
||||
<h1>
|
||||
title
|
||||
</h1>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Add"
|
||||
class="_icon-button_bh2qc_17"
|
||||
data-state="closed"
|
||||
id="radix-:r0:"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M3 5a2 2 0 0 1 2-2h6a1 1 0 1 1 0 2H5v14h14v-6a1 1 0 1 1 2 0v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5Z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="m8.023 14.711 1.331-3.992a1 1 0 0 1 1.656-.391l2.662 2.662a1 1 0 0 1-.391 1.655l-3.993 1.331a1 1 0 0 1-1.265-1.265Z"
|
||||
/>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M19.765 2.82a2 2 0 0 0-2.828 0L9.866 9.89c-.195.195-.341.42-.439.66a1.024 1.024 0 0 0-.073.168l-1.33 3.992a1 1 0 0 0 1.264 1.265l3.993-1.33c.059-.02.115-.045.167-.074.24-.097.466-.243.66-.438l7.072-7.071a2 2 0 0 0 0-2.829L19.765 2.82Zm-6.717 9.546 6.717-6.718-1.414-1.414-6.717 6.718 1.414 1.414Z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</header>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RoomListHeaderView /> should not display the compose menu 1`] = `
|
||||
<DocumentFragment>
|
||||
<header
|
||||
aria-label="Room options"
|
||||
class="mx_Flex mx_RoomListHeaderView"
|
||||
data-testid="room-list-header"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
|
||||
>
|
||||
<h1>
|
||||
title
|
||||
</h1>
|
||||
</header>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -2,16 +2,27 @@
|
||||
|
||||
exports[`<RoomListView /> should not render the RoomListSearch component when UIComponent.FilterContainer is at false 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
<section
|
||||
class="mx_RoomListView"
|
||||
data-testid="room-list-view"
|
||||
/>
|
||||
>
|
||||
<header
|
||||
aria-label="Room options"
|
||||
class="mx_Flex mx_RoomListHeaderView"
|
||||
data-testid="room-list-header"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
|
||||
>
|
||||
<h1>
|
||||
Home
|
||||
</h1>
|
||||
</header>
|
||||
</section>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RoomListView /> should render the RoomListSearch component when UIComponent.FilterContainer is at true 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
<section
|
||||
class="mx_RoomListView"
|
||||
data-testid="room-list-view"
|
||||
>
|
||||
@@ -71,6 +82,56 @@ exports[`<RoomListView /> should render the RoomListSearch component when UIComp
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<header
|
||||
aria-label="Room options"
|
||||
class="mx_Flex mx_RoomListHeaderView"
|
||||
data-testid="room-list-header"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
|
||||
>
|
||||
<h1>
|
||||
Home
|
||||
</h1>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Add"
|
||||
class="_icon-button_bh2qc_17"
|
||||
data-state="closed"
|
||||
id="radix-:r0:"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M3 5a2 2 0 0 1 2-2h6a1 1 0 1 1 0 2H5v14h14v-6a1 1 0 1 1 2 0v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5Z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="m8.023 14.711 1.331-3.992a1 1 0 0 1 1.656-.391l2.662 2.662a1 1 0 0 1-.391 1.655l-3.993 1.331a1 1 0 0 1-1.265-1.265Z"
|
||||
/>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M19.765 2.82a2 2 0 0 0-2.828 0L9.866 9.89c-.195.195-.341.42-.439.66a1.024 1.024 0 0 0-.073.168l-1.33 3.992a1 1 0 0 0 1.264 1.265l3.993-1.33c.059-.02.115-.045.167-.074.24-.097.466-.243.66-.438l7.072-7.071a2 2 0 0 0 0-2.829L19.765 2.82Zm-6.717 9.546 6.717-6.718-1.414-1.414-6.717 6.718 1.414 1.414Z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</header>
|
||||
</section>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user