/* Copyright 2023 Nordeck IT + Consulting GmbH Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import { act, fireEvent, render, screen, within } from "@testing-library/react"; import { EventTimeline, EventType, KnownMembership, MatrixError, MatrixEvent, Room, RoomMember, RoomStateEvent, } from "matrix-js-sdk/src/matrix"; import React from "react"; import ErrorDialog from "../../../../../../src/components/views/dialogs/ErrorDialog"; import { PeopleRoomSettingsTab } from "../../../../../../src/components/views/settings/tabs/room/PeopleRoomSettingsTab"; import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext"; import Modal from "../../../../../../src/Modal"; import { flushPromises, getMockClientWithEventEmitter } from "../../../../../test-utils"; describe("PeopleRoomSettingsTab", () => { const client = getMockClientWithEventEmitter({ getUserId: jest.fn(), invite: jest.fn(), kick: jest.fn(), mxcUrlToHttp: (mxcUrl: string) => mxcUrl, }); const roomId = "#ask-to-join:example.org"; const userId = "@alice:example.org"; const member = new RoomMember(roomId, userId); const room = new Room(roomId, client, userId); const state = room.getLiveTimeline().getState(EventTimeline.FORWARDS)!; const getButton = (name: "Approve" | "Deny" | "See less" | "See more") => screen.getByRole("button", { name }); const getComponent = (room: Room) => render( , ); const getGroup = () => screen.getByRole("group", { name: "Asking to join" }); const getParagraph = () => screen.getByRole("paragraph"); it("renders a heading", () => { getComponent(room); expect(screen.getByRole("heading")).toHaveTextContent("People"); }); it('renders a group "asking to join"', () => { getComponent(room); expect(getGroup()).toBeInTheDocument(); }); describe("without requests to join", () => { it('renders a paragraph "no requests"', () => { getComponent(room); expect(getParagraph()).toHaveTextContent("No requests"); }); }); describe("with requests to join", () => { const error = new MatrixError(); const knockUserId = "@albert.einstein:example.org"; const knockMember = new RoomMember(roomId, knockUserId); const reason = "There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle."; beforeEach(() => { jest.spyOn(Modal, "createDialog"); jest.spyOn(room, "canInvite").mockReturnValue(true); jest.spyOn(room, "getMember").mockReturnValue(member); jest.spyOn(room, "getMembersWithMembership").mockReturnValue([knockMember]); jest.spyOn(state, "hasSufficientPowerLevelFor").mockReturnValue(true); knockMember.setMembershipEvent( new MatrixEvent({ content: { avatar_url: "mxc://example.org/albert-einstein.png", displayname: "Albert Einstein", membership: KnownMembership.Knock, reason, }, origin_server_ts: -464140800000, type: EventType.RoomMember, }), ); }); it("renders requests fully", () => { getComponent(room); expect(getGroup()).toMatchSnapshot(); }); it("renders requests reduced", () => { knockMember.setMembershipEvent( new MatrixEvent({ content: { displayname: "albert.einstein", membership: KnownMembership.Knock, }, type: EventType.RoomMember, }), ); getComponent(room); expect(getGroup()).toMatchSnapshot(); }); it("allows to expand a reason", () => { getComponent(room); fireEvent.click(getButton("See more")); expect(within(getGroup()).getByRole("paragraph")).toHaveTextContent(reason); }); it("allows to collapse a reason", () => { getComponent(room); fireEvent.click(getButton("See more")); fireEvent.click(getButton("See less")); expect(getParagraph()).toHaveTextContent(`${reason.substring(0, 120)}…`); }); it("does not truncate a reason unnecessarily", () => { const reason = "I have no special talents. I am only passionately curious."; knockMember.setMembershipEvent( new MatrixEvent({ content: { displayname: "albert.einstein", membership: KnownMembership.Knock, reason, }, type: EventType.RoomMember, }), ); getComponent(room); expect(getParagraph()).toHaveTextContent(reason); }); it("disables the deny button if the power level is insufficient", () => { jest.spyOn(state, "hasSufficientPowerLevelFor").mockReturnValue(false); getComponent(room); expect(getButton("Deny")).toHaveAttribute("disabled"); }); it("calls kick on deny", () => { jest.spyOn(client, "kick").mockResolvedValue({}); getComponent(room); fireEvent.click(getButton("Deny")); expect(client.kick).toHaveBeenCalledWith(roomId, knockUserId); }); it("fails to deny a request", async () => { jest.spyOn(client, "kick").mockRejectedValue(error); getComponent(room); fireEvent.click(getButton("Deny")); await act(() => flushPromises()); expect(Modal.createDialog).toHaveBeenCalledWith(ErrorDialog, { title: error.name, description: error.message, }); }); it("succeeds to deny a request", () => { jest.spyOn(room, "getMembersWithMembership").mockReturnValue([]); getComponent(room); act(() => { room.emit(RoomStateEvent.Update, state); }); expect(getParagraph()).toHaveTextContent("No requests"); }); it("disables the approve button if the power level is insufficient", () => { jest.spyOn(room, "canInvite").mockReturnValue(false); getComponent(room); expect(getButton("Approve")).toHaveAttribute("disabled"); }); it("calls invite on approve", () => { jest.spyOn(client, "invite").mockResolvedValue({}); getComponent(room); fireEvent.click(getButton("Approve")); expect(client.invite).toHaveBeenCalledWith(roomId, knockUserId); }); it("fails to approve a request", async () => { jest.spyOn(client, "invite").mockRejectedValue(error); getComponent(room); fireEvent.click(getButton("Approve")); await act(() => flushPromises()); expect(Modal.createDialog).toHaveBeenCalledWith(ErrorDialog, { title: error.name, description: error.message, }); }); it("succeeds to approve a request", () => { jest.spyOn(room, "getMembersWithMembership").mockReturnValue([]); getComponent(room); act(() => { room.emit(RoomStateEvent.Update, state); }); expect(getParagraph()).toHaveTextContent("No requests"); }); }); });