988 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			988 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			TypeScript
		
	
	
/*
 | 
						|
Copyright 2022 The Matrix.org Foundation C.I.C.
 | 
						|
 | 
						|
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 React from "react";
 | 
						|
import { fireEvent, render, screen, waitFor, cleanup } from "@testing-library/react";
 | 
						|
import userEvent from "@testing-library/user-event";
 | 
						|
import { mocked } from "jest-mock";
 | 
						|
import { Room, User, MatrixClient, RoomMember, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
 | 
						|
import { Phase, VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
 | 
						|
import { DeviceTrustLevel, UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
 | 
						|
 | 
						|
import UserInfo, {
 | 
						|
    BanToggleButton,
 | 
						|
    DeviceItem,
 | 
						|
    disambiguateDevices,
 | 
						|
    getPowerLevels,
 | 
						|
    IDevice,
 | 
						|
    isMuted,
 | 
						|
    PowerLevelEditor,
 | 
						|
    RoomAdminToolsContainer,
 | 
						|
    RoomKickButton,
 | 
						|
    UserInfoHeader,
 | 
						|
    UserOptionsSection,
 | 
						|
} from "../../../../src/components/views/right_panel/UserInfo";
 | 
						|
import dis from "../../../../src/dispatcher/dispatcher";
 | 
						|
import { RightPanelPhases } from "../../../../src/stores/right-panel/RightPanelStorePhases";
 | 
						|
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
 | 
						|
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
 | 
						|
import MultiInviter from "../../../../src/utils/MultiInviter";
 | 
						|
import * as mockVerification from "../../../../src/verification";
 | 
						|
import Modal from "../../../../src/Modal";
 | 
						|
import { E2EStatus } from "../../../../src/utils/ShieldUtils";
 | 
						|
 | 
						|
jest.mock("../../../../src/dispatcher/dispatcher");
 | 
						|
 | 
						|
jest.mock("../../../../src/customisations/UserIdentifier", () => {
 | 
						|
    return {
 | 
						|
        getDisplayUserIdentifier: jest.fn().mockReturnValue("customUserIdentifier"),
 | 
						|
    };
 | 
						|
});
 | 
						|
 | 
						|
jest.mock("../../../../src/utils/DMRoomMap", () => {
 | 
						|
    const mock = {
 | 
						|
        getUserIdForRoomId: jest.fn(),
 | 
						|
        getDMRoomsForUserId: jest.fn(),
 | 
						|
    };
 | 
						|
 | 
						|
    return {
 | 
						|
        shared: jest.fn().mockReturnValue(mock),
 | 
						|
        sharedInstance: mock,
 | 
						|
    };
 | 
						|
});
 | 
						|
 | 
						|
const mockRoom = mocked({
 | 
						|
    roomId: "!fkfk",
 | 
						|
    getType: jest.fn().mockReturnValue(undefined),
 | 
						|
    isSpaceRoom: jest.fn().mockReturnValue(false),
 | 
						|
    getMember: jest.fn().mockReturnValue(undefined),
 | 
						|
    getMxcAvatarUrl: jest.fn().mockReturnValue("mock-avatar-url"),
 | 
						|
    name: "test room",
 | 
						|
    on: jest.fn(),
 | 
						|
    currentState: {
 | 
						|
        getStateEvents: jest.fn(),
 | 
						|
        on: jest.fn(),
 | 
						|
    },
 | 
						|
    getEventReadUpTo: jest.fn(),
 | 
						|
} as unknown as Room);
 | 
						|
 | 
						|
const mockClient = mocked({
 | 
						|
    getUser: jest.fn(),
 | 
						|
    isGuest: jest.fn().mockReturnValue(false),
 | 
						|
    isUserIgnored: jest.fn(),
 | 
						|
    isCryptoEnabled: jest.fn(),
 | 
						|
    getUserId: jest.fn(),
 | 
						|
    on: jest.fn(),
 | 
						|
    isSynapseAdministrator: jest.fn().mockResolvedValue(false),
 | 
						|
    isRoomEncrypted: jest.fn().mockReturnValue(false),
 | 
						|
    doesServerSupportUnstableFeature: jest.fn().mockReturnValue(false),
 | 
						|
    mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"),
 | 
						|
    removeListener: jest.fn(),
 | 
						|
    currentState: {
 | 
						|
        on: jest.fn(),
 | 
						|
    },
 | 
						|
    checkDeviceTrust: jest.fn(),
 | 
						|
    checkUserTrust: jest.fn(),
 | 
						|
    getRoom: jest.fn(),
 | 
						|
    credentials: {},
 | 
						|
    setPowerLevel: jest.fn(),
 | 
						|
} as unknown as MatrixClient);
 | 
						|
 | 
						|
const defaultUserId = "@test:test";
 | 
						|
const defaultUser = new User(defaultUserId);
 | 
						|
 | 
						|
beforeEach(() => {
 | 
						|
    jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
 | 
						|
});
 | 
						|
 | 
						|
afterEach(() => {
 | 
						|
    mockClient.getUser.mockClear().mockReturnValue({} as unknown as User);
 | 
						|
});
 | 
						|
 | 
						|
describe("<UserInfo />", () => {
 | 
						|
    const verificationRequest = {
 | 
						|
        pending: true,
 | 
						|
        on: jest.fn(),
 | 
						|
        phase: Phase.Ready,
 | 
						|
        channel: { transactionId: 1 },
 | 
						|
        otherPartySupportsMethod: jest.fn(),
 | 
						|
        off: jest.fn(),
 | 
						|
    } as unknown as VerificationRequest;
 | 
						|
 | 
						|
    const defaultProps = {
 | 
						|
        user: defaultUser,
 | 
						|
        // idk what is wrong with this type
 | 
						|
        phase: RightPanelPhases.RoomMemberInfo as RightPanelPhases.RoomMemberInfo,
 | 
						|
        onClose: jest.fn(),
 | 
						|
    };
 | 
						|
 | 
						|
    const renderComponent = (props = {}) => {
 | 
						|
        const Wrapper = (wrapperProps = {}) => {
 | 
						|
            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
 | 
						|
        };
 | 
						|
 | 
						|
        return render(<UserInfo {...defaultProps} {...props} />, {
 | 
						|
            wrapper: Wrapper,
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    it("closes on close button click", async () => {
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        await userEvent.click(screen.getByTestId("base-card-close-button"));
 | 
						|
 | 
						|
        expect(defaultProps.onClose).toHaveBeenCalled();
 | 
						|
    });
 | 
						|
 | 
						|
    describe("without a room", () => {
 | 
						|
        it("does not render space header", () => {
 | 
						|
            renderComponent();
 | 
						|
            expect(screen.queryByTestId("space-header")).not.toBeInTheDocument();
 | 
						|
        });
 | 
						|
 | 
						|
        it("renders user info", () => {
 | 
						|
            renderComponent();
 | 
						|
            expect(screen.getByRole("heading", { name: defaultUserId })).toBeInTheDocument();
 | 
						|
        });
 | 
						|
 | 
						|
        it("renders encryption info panel without pending verification", () => {
 | 
						|
            renderComponent({ phase: RightPanelPhases.EncryptionPanel });
 | 
						|
            expect(screen.getByRole("heading", { name: /encryption/i })).toBeInTheDocument();
 | 
						|
        });
 | 
						|
 | 
						|
        it("renders encryption verification panel with pending verification", () => {
 | 
						|
            renderComponent({ phase: RightPanelPhases.EncryptionPanel, verificationRequest });
 | 
						|
 | 
						|
            expect(screen.queryByRole("heading", { name: /encryption/i })).not.toBeInTheDocument();
 | 
						|
            // the verificationRequest has phase of Phase.Ready but .otherPartySupportsMethod
 | 
						|
            // will not return true, so we expect to see the noCommonMethod error from VerificationPanel
 | 
						|
            expect(screen.getByText(/try with a different client/i)).toBeInTheDocument();
 | 
						|
        });
 | 
						|
 | 
						|
        it("renders close button correctly when encryption panel with a pending verification request", () => {
 | 
						|
            renderComponent({ phase: RightPanelPhases.EncryptionPanel, verificationRequest });
 | 
						|
            expect(screen.getByTestId("base-card-close-button")).toHaveAttribute("title", "Cancel");
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    describe("with a room", () => {
 | 
						|
        it("renders user info", () => {
 | 
						|
            renderComponent({ room: mockRoom });
 | 
						|
            expect(screen.getByRole("heading", { name: defaultUserId })).toBeInTheDocument();
 | 
						|
        });
 | 
						|
 | 
						|
        it("does not render space header when room is not a space room", () => {
 | 
						|
            renderComponent({ room: mockRoom });
 | 
						|
            expect(screen.queryByTestId("space-header")).not.toBeInTheDocument();
 | 
						|
        });
 | 
						|
 | 
						|
        it("renders space header when room is a space room", () => {
 | 
						|
            const spaceRoom = {
 | 
						|
                ...mockRoom,
 | 
						|
                isSpaceRoom: jest.fn().mockReturnValue(true),
 | 
						|
            };
 | 
						|
            renderComponent({ room: spaceRoom });
 | 
						|
            expect(screen.getByTestId("space-header")).toBeInTheDocument();
 | 
						|
        });
 | 
						|
 | 
						|
        it("renders encryption info panel without pending verification", () => {
 | 
						|
            renderComponent({ phase: RightPanelPhases.EncryptionPanel, room: mockRoom });
 | 
						|
            expect(screen.getByRole("heading", { name: /encryption/i })).toBeInTheDocument();
 | 
						|
        });
 | 
						|
 | 
						|
        it("renders encryption verification panel with pending verification", () => {
 | 
						|
            renderComponent({ phase: RightPanelPhases.EncryptionPanel, verificationRequest, room: mockRoom });
 | 
						|
 | 
						|
            expect(screen.queryByRole("heading", { name: /encryption/i })).not.toBeInTheDocument();
 | 
						|
            // the verificationRequest has phase of Phase.Ready but .otherPartySupportsMethod
 | 
						|
            // will not return true, so we expect to see the noCommonMethod error from VerificationPanel
 | 
						|
            expect(screen.getByText(/try with a different client/i)).toBeInTheDocument();
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("<UserInfoHeader />", () => {
 | 
						|
    const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId);
 | 
						|
 | 
						|
    const defaultProps = {
 | 
						|
        member: defaultMember,
 | 
						|
        roomId: mockRoom.roomId,
 | 
						|
    };
 | 
						|
 | 
						|
    const renderComponent = (props = {}) => {
 | 
						|
        const Wrapper = (wrapperProps = {}) => {
 | 
						|
            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
 | 
						|
        };
 | 
						|
 | 
						|
        return render(<UserInfoHeader {...defaultProps} {...props} />, {
 | 
						|
            wrapper: Wrapper,
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    it("does not render an e2e icon in the header if e2eStatus prop is undefined", () => {
 | 
						|
        renderComponent();
 | 
						|
        const header = screen.getByRole("heading", { name: defaultUserId });
 | 
						|
 | 
						|
        expect(header.getElementsByClassName("mx_E2EIcon")).toHaveLength(0);
 | 
						|
    });
 | 
						|
 | 
						|
    it("renders an e2e icon in the header if e2eStatus prop is defined", () => {
 | 
						|
        renderComponent({ e2eStatus: E2EStatus.Normal });
 | 
						|
        const header = screen.getByRole("heading", { name: defaultUserId });
 | 
						|
 | 
						|
        expect(header.getElementsByClassName("mx_E2EIcon")).toHaveLength(1);
 | 
						|
    });
 | 
						|
 | 
						|
    it("renders custom user identifiers in the header", () => {
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.getByText("customUserIdentifier")).toBeInTheDocument();
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("<DeviceItem />", () => {
 | 
						|
    const device: IDevice = { deviceId: "deviceId", getDisplayName: () => "deviceName" };
 | 
						|
    const defaultProps = {
 | 
						|
        userId: defaultUserId,
 | 
						|
        device,
 | 
						|
    };
 | 
						|
 | 
						|
    const renderComponent = (props = {}) => {
 | 
						|
        const Wrapper = (wrapperProps = {}) => {
 | 
						|
            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
 | 
						|
        };
 | 
						|
 | 
						|
        return render(<DeviceItem {...defaultProps} {...props} />, {
 | 
						|
            wrapper: Wrapper,
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    const setMockUserTrust = (isVerified = false) => {
 | 
						|
        mockClient.checkUserTrust.mockReturnValue({ isVerified: () => isVerified } as UserTrustLevel);
 | 
						|
    };
 | 
						|
    const setMockDeviceTrust = (isVerified = false, isCrossSigningVerified = false) => {
 | 
						|
        mockClient.checkDeviceTrust.mockReturnValue({
 | 
						|
            isVerified: () => isVerified,
 | 
						|
            isCrossSigningVerified: () => isCrossSigningVerified,
 | 
						|
        } as DeviceTrustLevel);
 | 
						|
    };
 | 
						|
 | 
						|
    const mockVerifyDevice = jest.spyOn(mockVerification, "verifyDevice");
 | 
						|
 | 
						|
    beforeEach(() => {
 | 
						|
        setMockUserTrust();
 | 
						|
        setMockDeviceTrust();
 | 
						|
    });
 | 
						|
 | 
						|
    afterEach(() => {
 | 
						|
        mockClient.checkDeviceTrust.mockReset();
 | 
						|
        mockClient.checkUserTrust.mockReset();
 | 
						|
        mockVerifyDevice.mockClear();
 | 
						|
    });
 | 
						|
 | 
						|
    afterAll(() => {
 | 
						|
        mockVerifyDevice.mockRestore();
 | 
						|
    });
 | 
						|
 | 
						|
    it("with unverified user and device, displays button without a label", () => {
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.getByRole("button", { name: device.getDisplayName() })).toBeInTheDocument;
 | 
						|
        expect(screen.queryByText(/trusted/i)).not.toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("with verified user only, displays button with a 'Not trusted' label", () => {
 | 
						|
        setMockUserTrust(true);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.getByRole("button", { name: `${device.getDisplayName()} Not trusted` })).toBeInTheDocument;
 | 
						|
    });
 | 
						|
 | 
						|
    it("with verified device only, displays no button without a label", () => {
 | 
						|
        setMockDeviceTrust(true);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.getByText(device.getDisplayName())).toBeInTheDocument();
 | 
						|
        expect(screen.queryByText(/trusted/)).not.toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("when userId is the same as userId from client, uses isCrossSigningVerified to determine if button is shown", () => {
 | 
						|
        mockClient.getUserId.mockReturnValueOnce(defaultUserId);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        // set trust to be false for isVerified, true for isCrossSigningVerified
 | 
						|
        setMockDeviceTrust(false, true);
 | 
						|
 | 
						|
        // expect to see no button in this case
 | 
						|
        expect(screen.queryByRole("button")).not.toBeInTheDocument;
 | 
						|
        expect(screen.getByText(device.getDisplayName())).toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("with verified user and device, displays no button and a 'Trusted' label", () => {
 | 
						|
        setMockUserTrust(true);
 | 
						|
        setMockDeviceTrust(true);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.queryByRole("button")).not.toBeInTheDocument;
 | 
						|
        expect(screen.getByText(device.getDisplayName())).toBeInTheDocument();
 | 
						|
        expect(screen.getByText("Trusted")).toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("does not call verifyDevice if client.getUser returns null", async () => {
 | 
						|
        mockClient.getUser.mockReturnValueOnce(null);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        const button = screen.getByRole("button", { name: device.getDisplayName() });
 | 
						|
        expect(button).toBeInTheDocument;
 | 
						|
        await userEvent.click(button);
 | 
						|
 | 
						|
        expect(mockVerifyDevice).not.toHaveBeenCalled();
 | 
						|
    });
 | 
						|
 | 
						|
    it("calls verifyDevice if client.getUser returns an object", async () => {
 | 
						|
        mockClient.getUser.mockReturnValueOnce(defaultUser);
 | 
						|
        // set mock return of isGuest to short circuit verifyDevice call to avoid
 | 
						|
        // even more mocking
 | 
						|
        mockClient.isGuest.mockReturnValueOnce(true);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        const button = screen.getByRole("button", { name: device.getDisplayName() });
 | 
						|
        expect(button).toBeInTheDocument;
 | 
						|
        await userEvent.click(button);
 | 
						|
 | 
						|
        expect(mockVerifyDevice).toHaveBeenCalledTimes(1);
 | 
						|
        expect(mockVerifyDevice).toHaveBeenCalledWith(defaultUser, device);
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("<UserOptionsSection />", () => {
 | 
						|
    const member = new RoomMember(mockRoom.roomId, defaultUserId);
 | 
						|
    const defaultProps = { member, isIgnored: false, canInvite: false, isSpace: false };
 | 
						|
 | 
						|
    const renderComponent = (props = {}) => {
 | 
						|
        const Wrapper = (wrapperProps = {}) => {
 | 
						|
            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
 | 
						|
        };
 | 
						|
 | 
						|
        return render(<UserOptionsSection {...defaultProps} {...props} />, {
 | 
						|
            wrapper: Wrapper,
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    const inviteSpy = jest.spyOn(MultiInviter.prototype, "invite");
 | 
						|
 | 
						|
    beforeEach(() => {
 | 
						|
        inviteSpy.mockReset();
 | 
						|
    });
 | 
						|
 | 
						|
    afterAll(() => {
 | 
						|
        inviteSpy.mockRestore();
 | 
						|
    });
 | 
						|
 | 
						|
    it("always shows share user button", () => {
 | 
						|
        renderComponent();
 | 
						|
        expect(screen.getByRole("button", { name: /share link to user/i })).toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("does not show ignore or direct message buttons when member userId matches client userId ", () => {
 | 
						|
        mockClient.getUserId.mockReturnValueOnce(member.userId);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.queryByRole("button", { name: /ignore/i })).not.toBeInTheDocument();
 | 
						|
        expect(screen.queryByRole("button", { name: /message/i })).not.toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("shows ignore, direct message and mention buttons when member userId does not match client userId ", () => {
 | 
						|
        // call to client.getUserId returns undefined, which will not match member.userId
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.getByRole("button", { name: /ignore/i })).toBeInTheDocument();
 | 
						|
        expect(screen.getByRole("button", { name: /message/i })).toBeInTheDocument();
 | 
						|
        expect(screen.getByRole("button", { name: /mention/i })).toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("when call to client.getRoom is null, does not show read receipt button", () => {
 | 
						|
        mockClient.getRoom.mockReturnValueOnce(null);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.queryByRole("button", { name: /jump to read receipt/i })).not.toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("when call to client.getRoom is non-null and room.getEventReadUpTo is null, does not show read receipt button", () => {
 | 
						|
        mockRoom.getEventReadUpTo.mockReturnValueOnce(null);
 | 
						|
        mockClient.getRoom.mockReturnValueOnce(mockRoom);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.queryByRole("button", { name: /jump to read receipt/i })).not.toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("when calls to client.getRoom and room.getEventReadUpTo are non-null, shows read receipt button", () => {
 | 
						|
        mockRoom.getEventReadUpTo.mockReturnValueOnce("1234");
 | 
						|
        mockClient.getRoom.mockReturnValueOnce(mockRoom);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.getByRole("button", { name: /jump to read receipt/i })).toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("clicking the read receipt button calls dispatch with correct event_id", async () => {
 | 
						|
        const mockEventId = "1234";
 | 
						|
        mockRoom.getEventReadUpTo.mockReturnValue(mockEventId);
 | 
						|
        mockClient.getRoom.mockReturnValue(mockRoom);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        const readReceiptButton = screen.getByRole("button", { name: /jump to read receipt/i });
 | 
						|
 | 
						|
        expect(readReceiptButton).toBeInTheDocument();
 | 
						|
        await userEvent.click(readReceiptButton);
 | 
						|
        expect(dis.dispatch).toHaveBeenCalledWith({
 | 
						|
            action: "view_room",
 | 
						|
            event_id: mockEventId,
 | 
						|
            highlighted: true,
 | 
						|
            metricsTrigger: undefined,
 | 
						|
            room_id: "!fkfk",
 | 
						|
        });
 | 
						|
 | 
						|
        mockRoom.getEventReadUpTo.mockReset();
 | 
						|
        mockClient.getRoom.mockReset();
 | 
						|
    });
 | 
						|
 | 
						|
    it("firing the read receipt event handler with a null event_id calls dispatch with undefined not null", async () => {
 | 
						|
        const mockEventId = "1234";
 | 
						|
        // the first call is the check to see if we should render the button, second call is
 | 
						|
        // when the button is clicked
 | 
						|
        mockRoom.getEventReadUpTo.mockReturnValueOnce(mockEventId).mockReturnValueOnce(null);
 | 
						|
        mockClient.getRoom.mockReturnValue(mockRoom);
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        const readReceiptButton = screen.getByRole("button", { name: /jump to read receipt/i });
 | 
						|
 | 
						|
        expect(readReceiptButton).toBeInTheDocument();
 | 
						|
        await userEvent.click(readReceiptButton);
 | 
						|
        expect(dis.dispatch).toHaveBeenCalledWith({
 | 
						|
            action: "view_room",
 | 
						|
            event_id: undefined,
 | 
						|
            highlighted: true,
 | 
						|
            metricsTrigger: undefined,
 | 
						|
            room_id: "!fkfk",
 | 
						|
        });
 | 
						|
 | 
						|
        mockClient.getRoom.mockReset();
 | 
						|
    });
 | 
						|
 | 
						|
    it("does not show the invite button when canInvite is false", () => {
 | 
						|
        renderComponent();
 | 
						|
        expect(screen.queryByRole("button", { name: /invite/i })).not.toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("shows the invite button when canInvite is true", () => {
 | 
						|
        renderComponent({ canInvite: true });
 | 
						|
        expect(screen.getByRole("button", { name: /invite/i })).toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("clicking the invite button will call MultiInviter.invite", async () => {
 | 
						|
        // to save mocking, we will reject the call to .invite
 | 
						|
        const mockErrorMessage = new Error("test error message");
 | 
						|
        inviteSpy.mockRejectedValue(mockErrorMessage);
 | 
						|
 | 
						|
        // render the component and click the button
 | 
						|
        renderComponent({ canInvite: true });
 | 
						|
        const inviteButton = screen.getByRole("button", { name: /invite/i });
 | 
						|
        expect(inviteButton).toBeInTheDocument();
 | 
						|
        await userEvent.click(inviteButton);
 | 
						|
 | 
						|
        // check that we have called .invite
 | 
						|
        expect(inviteSpy).toHaveBeenCalledWith([member.userId]);
 | 
						|
 | 
						|
        // check that the test error message is displayed
 | 
						|
        await waitFor(() => {
 | 
						|
            expect(screen.getByText(mockErrorMessage.message)).toBeInTheDocument();
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    it("if calling .invite throws something strange, show default error message", async () => {
 | 
						|
        inviteSpy.mockRejectedValue({ this: "could be anything" });
 | 
						|
 | 
						|
        // render the component and click the button
 | 
						|
        renderComponent({ canInvite: true });
 | 
						|
        const inviteButton = screen.getByRole("button", { name: /invite/i });
 | 
						|
        expect(inviteButton).toBeInTheDocument();
 | 
						|
        await userEvent.click(inviteButton);
 | 
						|
 | 
						|
        // check that the default test error message is displayed
 | 
						|
        await waitFor(() => {
 | 
						|
            expect(screen.getByText(/operation failed/i)).toBeInTheDocument();
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    it("calling .invite with a null roomId still calls .invite and shows default error message", async () => {
 | 
						|
        inviteSpy.mockRejectedValue({ this: "could be anything" });
 | 
						|
 | 
						|
        // render the component and click the button
 | 
						|
        renderComponent({ canInvite: true, member: { ...member, roomId: null } });
 | 
						|
        const inviteButton = screen.getByRole("button", { name: /invite/i });
 | 
						|
        expect(inviteButton).toBeInTheDocument();
 | 
						|
        await userEvent.click(inviteButton);
 | 
						|
 | 
						|
        expect(inviteSpy).toHaveBeenCalledTimes(1);
 | 
						|
 | 
						|
        // check that the default test error message is displayed
 | 
						|
        await waitFor(() => {
 | 
						|
            expect(screen.getByText(/operation failed/i)).toBeInTheDocument();
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("<PowerLevelEditor />", () => {
 | 
						|
    const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId);
 | 
						|
 | 
						|
    const defaultProps = {
 | 
						|
        user: defaultMember,
 | 
						|
        room: mockRoom,
 | 
						|
        roomPermissions: {
 | 
						|
            modifyLevelMax: 100,
 | 
						|
            canEdit: false,
 | 
						|
            canInvite: false,
 | 
						|
        },
 | 
						|
    };
 | 
						|
 | 
						|
    const renderComponent = (props = {}) => {
 | 
						|
        const Wrapper = (wrapperProps = {}) => {
 | 
						|
            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
 | 
						|
        };
 | 
						|
 | 
						|
        return render(<PowerLevelEditor {...defaultProps} {...props} />, {
 | 
						|
            wrapper: Wrapper,
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    it("renders a power level combobox", () => {
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        expect(screen.getByRole("combobox", { name: "Power level" })).toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("renders a combobox and attempts to change power level on change of the combobox", async () => {
 | 
						|
        const startPowerLevel = 999;
 | 
						|
        const powerLevelEvent = new MatrixEvent({
 | 
						|
            type: EventType.RoomPowerLevels,
 | 
						|
            content: { users: { [defaultUserId]: startPowerLevel }, users_default: 1 },
 | 
						|
        });
 | 
						|
        mockRoom.currentState.getStateEvents.mockReturnValue(powerLevelEvent);
 | 
						|
        mockClient.getUserId.mockReturnValueOnce(defaultUserId);
 | 
						|
        mockClient.setPowerLevel.mockResolvedValueOnce({ event_id: "123" });
 | 
						|
        renderComponent();
 | 
						|
 | 
						|
        const changedPowerLevel = 100;
 | 
						|
 | 
						|
        fireEvent.change(screen.getByRole("combobox", { name: "Power level" }), {
 | 
						|
            target: { value: changedPowerLevel },
 | 
						|
        });
 | 
						|
 | 
						|
        await screen.findByText("Demote", { exact: true });
 | 
						|
 | 
						|
        // firing the event will raise a dialog warning about self demotion, wait for this to appear then click on it
 | 
						|
        await userEvent.click(await screen.findByText("Demote", { exact: true }));
 | 
						|
        expect(mockClient.setPowerLevel).toHaveBeenCalledTimes(1);
 | 
						|
        expect(mockClient.setPowerLevel).toHaveBeenCalledWith(
 | 
						|
            mockRoom.roomId,
 | 
						|
            defaultMember.userId,
 | 
						|
            changedPowerLevel,
 | 
						|
            powerLevelEvent,
 | 
						|
        );
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("<RoomKickButton />", () => {
 | 
						|
    const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId);
 | 
						|
    const memberWithInviteMembership = { ...defaultMember, membership: "invite" };
 | 
						|
    const memberWithJoinMembership = { ...defaultMember, membership: "join" };
 | 
						|
 | 
						|
    const defaultProps = { room: mockRoom, member: defaultMember, startUpdating: jest.fn(), stopUpdating: jest.fn() };
 | 
						|
 | 
						|
    const renderComponent = (props = {}) => {
 | 
						|
        const Wrapper = (wrapperProps = {}) => {
 | 
						|
            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
 | 
						|
        };
 | 
						|
 | 
						|
        return render(<RoomKickButton {...defaultProps} {...props} />, {
 | 
						|
            wrapper: Wrapper,
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    const createDialogSpy: jest.SpyInstance = jest.spyOn(Modal, "createDialog");
 | 
						|
 | 
						|
    afterEach(() => {
 | 
						|
        createDialogSpy.mockReset();
 | 
						|
    });
 | 
						|
 | 
						|
    it("renders nothing if member.membership is undefined", () => {
 | 
						|
        // .membership is undefined in our member by default
 | 
						|
        const { container } = renderComponent();
 | 
						|
        expect(container).toBeEmptyDOMElement();
 | 
						|
    });
 | 
						|
 | 
						|
    it("renders something if member.membership is 'invite' or 'join'", () => {
 | 
						|
        let result = renderComponent({ member: memberWithInviteMembership });
 | 
						|
        expect(result.container).not.toBeEmptyDOMElement();
 | 
						|
 | 
						|
        cleanup();
 | 
						|
 | 
						|
        result = renderComponent({ member: memberWithJoinMembership });
 | 
						|
        expect(result.container).not.toBeEmptyDOMElement();
 | 
						|
    });
 | 
						|
 | 
						|
    it("renders the correct label", () => {
 | 
						|
        // test for room
 | 
						|
        renderComponent({ member: memberWithJoinMembership });
 | 
						|
        expect(screen.getByText(/remove from room/i)).toBeInTheDocument();
 | 
						|
        cleanup();
 | 
						|
 | 
						|
        renderComponent({ member: memberWithInviteMembership });
 | 
						|
        expect(screen.getByText(/disinvite from room/i)).toBeInTheDocument();
 | 
						|
        cleanup();
 | 
						|
 | 
						|
        // test for space
 | 
						|
        mockRoom.isSpaceRoom.mockReturnValue(true);
 | 
						|
        renderComponent({ member: memberWithJoinMembership });
 | 
						|
        expect(screen.getByText(/remove from space/i)).toBeInTheDocument();
 | 
						|
        cleanup();
 | 
						|
 | 
						|
        renderComponent({ member: memberWithInviteMembership });
 | 
						|
        expect(screen.getByText(/disinvite from space/i)).toBeInTheDocument();
 | 
						|
        cleanup();
 | 
						|
        mockRoom.isSpaceRoom.mockReturnValue(false);
 | 
						|
    });
 | 
						|
 | 
						|
    it("clicking the kick button calls Modal.createDialog with the correct arguments", async () => {
 | 
						|
        createDialogSpy.mockReturnValueOnce({ finished: Promise.resolve([]), close: jest.fn() });
 | 
						|
 | 
						|
        renderComponent({ member: memberWithInviteMembership });
 | 
						|
        await userEvent.click(screen.getByText(/disinvite from/i));
 | 
						|
 | 
						|
        // check the last call arguments and the presence of the spaceChildFilter callback
 | 
						|
        expect(createDialogSpy).toHaveBeenLastCalledWith(
 | 
						|
            expect.any(Function),
 | 
						|
            expect.objectContaining({ spaceChildFilter: expect.any(Function) }),
 | 
						|
            undefined,
 | 
						|
        );
 | 
						|
 | 
						|
        // test the spaceChildFilter callback
 | 
						|
        const callback = createDialogSpy.mock.lastCall[1].spaceChildFilter;
 | 
						|
 | 
						|
        // make dummy values for myMember and theirMember, then we will test
 | 
						|
        // null vs their member followed by
 | 
						|
        // my member vs their member
 | 
						|
        const mockMyMember = { powerLevel: 1 };
 | 
						|
        const mockTheirMember = { membership: "invite", powerLevel: 0 };
 | 
						|
 | 
						|
        const mockRoom = {
 | 
						|
            getMember: jest
 | 
						|
                .fn()
 | 
						|
                .mockReturnValueOnce(null)
 | 
						|
                .mockReturnValueOnce(mockTheirMember)
 | 
						|
                .mockReturnValueOnce(mockMyMember)
 | 
						|
                .mockReturnValueOnce(mockTheirMember),
 | 
						|
            currentState: {
 | 
						|
                hasSufficientPowerLevelFor: jest.fn().mockReturnValue(true),
 | 
						|
            },
 | 
						|
        };
 | 
						|
 | 
						|
        expect(callback(mockRoom)).toBe(null);
 | 
						|
        expect(callback(mockRoom)).toBe(true);
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("<BanToggleButton />", () => {
 | 
						|
    const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId);
 | 
						|
    const memberWithBanMembership = { ...defaultMember, membership: "ban" };
 | 
						|
 | 
						|
    const defaultProps = { room: mockRoom, member: defaultMember, startUpdating: jest.fn(), stopUpdating: jest.fn() };
 | 
						|
 | 
						|
    const renderComponent = (props = {}) => {
 | 
						|
        const Wrapper = (wrapperProps = {}) => {
 | 
						|
            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
 | 
						|
        };
 | 
						|
 | 
						|
        return render(<BanToggleButton {...defaultProps} {...props} />, {
 | 
						|
            wrapper: Wrapper,
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    const createDialogSpy: jest.SpyInstance = jest.spyOn(Modal, "createDialog");
 | 
						|
 | 
						|
    afterEach(() => {
 | 
						|
        createDialogSpy.mockReset();
 | 
						|
    });
 | 
						|
 | 
						|
    it("renders the correct labels for banned and unbanned members", () => {
 | 
						|
        // test for room
 | 
						|
        // defaultMember is not banned
 | 
						|
        renderComponent();
 | 
						|
        expect(screen.getByText("Ban from room")).toBeInTheDocument();
 | 
						|
        cleanup();
 | 
						|
 | 
						|
        renderComponent({ member: memberWithBanMembership });
 | 
						|
        expect(screen.getByText("Unban from room")).toBeInTheDocument();
 | 
						|
        cleanup();
 | 
						|
 | 
						|
        // test for space
 | 
						|
        mockRoom.isSpaceRoom.mockReturnValue(true);
 | 
						|
        renderComponent();
 | 
						|
        expect(screen.getByText("Ban from space")).toBeInTheDocument();
 | 
						|
        cleanup();
 | 
						|
 | 
						|
        renderComponent({ member: memberWithBanMembership });
 | 
						|
        expect(screen.getByText("Unban from space")).toBeInTheDocument();
 | 
						|
        cleanup();
 | 
						|
        mockRoom.isSpaceRoom.mockReturnValue(false);
 | 
						|
    });
 | 
						|
 | 
						|
    it("clicking the ban or unban button calls Modal.createDialog with the correct arguments if user is not banned", async () => {
 | 
						|
        createDialogSpy.mockReturnValueOnce({ finished: Promise.resolve([]), close: jest.fn() });
 | 
						|
 | 
						|
        renderComponent();
 | 
						|
        await userEvent.click(screen.getByText(/ban from/i));
 | 
						|
 | 
						|
        // check the last call arguments and the presence of the spaceChildFilter callback
 | 
						|
        expect(createDialogSpy).toHaveBeenLastCalledWith(
 | 
						|
            expect.any(Function),
 | 
						|
            expect.objectContaining({ spaceChildFilter: expect.any(Function) }),
 | 
						|
            undefined,
 | 
						|
        );
 | 
						|
 | 
						|
        // test the spaceChildFilter callback
 | 
						|
        const callback = createDialogSpy.mock.lastCall[1].spaceChildFilter;
 | 
						|
 | 
						|
        // make dummy values for myMember and theirMember, then we will test
 | 
						|
        // null vs their member followed by
 | 
						|
        // truthy my member vs their member
 | 
						|
        const mockMyMember = { powerLevel: 1 };
 | 
						|
        const mockTheirMember = { membership: "is not ban", powerLevel: 0 };
 | 
						|
 | 
						|
        const mockRoom = {
 | 
						|
            getMember: jest
 | 
						|
                .fn()
 | 
						|
                .mockReturnValueOnce(null)
 | 
						|
                .mockReturnValueOnce(mockTheirMember)
 | 
						|
                .mockReturnValueOnce(mockMyMember)
 | 
						|
                .mockReturnValueOnce(mockTheirMember),
 | 
						|
            currentState: {
 | 
						|
                hasSufficientPowerLevelFor: jest.fn().mockReturnValue(true),
 | 
						|
            },
 | 
						|
        };
 | 
						|
 | 
						|
        expect(callback(mockRoom)).toBe(null);
 | 
						|
        expect(callback(mockRoom)).toBe(true);
 | 
						|
    });
 | 
						|
 | 
						|
    it("clicking the ban or unban button calls Modal.createDialog with the correct arguments if user _is_ banned", async () => {
 | 
						|
        createDialogSpy.mockReturnValueOnce({ finished: Promise.resolve([]), close: jest.fn() });
 | 
						|
 | 
						|
        renderComponent({ member: memberWithBanMembership });
 | 
						|
        await userEvent.click(screen.getByText(/ban from/i));
 | 
						|
 | 
						|
        // check the last call arguments and the presence of the spaceChildFilter callback
 | 
						|
        expect(createDialogSpy).toHaveBeenLastCalledWith(
 | 
						|
            expect.any(Function),
 | 
						|
            expect.objectContaining({ spaceChildFilter: expect.any(Function) }),
 | 
						|
            undefined,
 | 
						|
        );
 | 
						|
 | 
						|
        // test the spaceChildFilter callback
 | 
						|
        const callback = createDialogSpy.mock.lastCall[1].spaceChildFilter;
 | 
						|
 | 
						|
        // make dummy values for myMember and theirMember, then we will test
 | 
						|
        // null vs their member followed by
 | 
						|
        // my member vs their member
 | 
						|
        const mockMyMember = { powerLevel: 1 };
 | 
						|
        const mockTheirMember = { membership: "ban", powerLevel: 0 };
 | 
						|
 | 
						|
        const mockRoom = {
 | 
						|
            getMember: jest
 | 
						|
                .fn()
 | 
						|
                .mockReturnValueOnce(null)
 | 
						|
                .mockReturnValueOnce(mockTheirMember)
 | 
						|
                .mockReturnValueOnce(mockMyMember)
 | 
						|
                .mockReturnValueOnce(mockTheirMember),
 | 
						|
            currentState: {
 | 
						|
                hasSufficientPowerLevelFor: jest.fn().mockReturnValue(true),
 | 
						|
            },
 | 
						|
        };
 | 
						|
 | 
						|
        expect(callback(mockRoom)).toBe(null);
 | 
						|
        expect(callback(mockRoom)).toBe(true);
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("<RoomAdminToolsContainer />", () => {
 | 
						|
    const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId);
 | 
						|
    defaultMember.membership = "invite";
 | 
						|
 | 
						|
    const defaultProps = {
 | 
						|
        room: mockRoom,
 | 
						|
        member: defaultMember,
 | 
						|
        startUpdating: jest.fn(),
 | 
						|
        stopUpdating: jest.fn(),
 | 
						|
        powerLevels: {},
 | 
						|
    };
 | 
						|
 | 
						|
    const renderComponent = (props = {}) => {
 | 
						|
        const Wrapper = (wrapperProps = {}) => {
 | 
						|
            return <MatrixClientContext.Provider value={mockClient} {...wrapperProps} />;
 | 
						|
        };
 | 
						|
 | 
						|
        return render(<RoomAdminToolsContainer {...defaultProps} {...props} />, {
 | 
						|
            wrapper: Wrapper,
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    it("returns a single empty div if room.getMember is falsy", () => {
 | 
						|
        const { asFragment } = renderComponent();
 | 
						|
        expect(asFragment()).toMatchInlineSnapshot(`
 | 
						|
            <DocumentFragment>
 | 
						|
              <div />
 | 
						|
            </DocumentFragment>
 | 
						|
        `);
 | 
						|
    });
 | 
						|
 | 
						|
    it("can return a single empty div in case where room.getMember is not falsy", () => {
 | 
						|
        mockRoom.getMember.mockReturnValueOnce(defaultMember);
 | 
						|
        const { asFragment } = renderComponent();
 | 
						|
        expect(asFragment()).toMatchInlineSnapshot(`
 | 
						|
            <DocumentFragment>
 | 
						|
              <div />
 | 
						|
            </DocumentFragment>
 | 
						|
        `);
 | 
						|
    });
 | 
						|
 | 
						|
    it("returns kick, redact messages, ban buttons if conditions met", () => {
 | 
						|
        const mockMeMember = new RoomMember(mockRoom.roomId, "arbitraryId");
 | 
						|
        mockMeMember.powerLevel = 51; // defaults to 50
 | 
						|
        mockRoom.getMember.mockReturnValueOnce(mockMeMember);
 | 
						|
 | 
						|
        const defaultMemberWithPowerLevel = { ...defaultMember, powerLevel: 0 };
 | 
						|
 | 
						|
        renderComponent({ member: defaultMemberWithPowerLevel });
 | 
						|
 | 
						|
        expect(screen.getByRole("heading", { name: /admin tools/i })).toBeInTheDocument();
 | 
						|
        expect(screen.getByText(/disinvite from room/i)).toBeInTheDocument();
 | 
						|
        expect(screen.getByText(/ban from room/i)).toBeInTheDocument();
 | 
						|
        expect(screen.getByText(/remove recent messages/i)).toBeInTheDocument();
 | 
						|
    });
 | 
						|
 | 
						|
    it("returns mute toggle button if conditions met", () => {
 | 
						|
        const mockMeMember = new RoomMember(mockRoom.roomId, "arbitraryId");
 | 
						|
        mockMeMember.powerLevel = 51; // defaults to 50
 | 
						|
        mockRoom.getMember.mockReturnValueOnce(mockMeMember);
 | 
						|
 | 
						|
        const defaultMemberWithPowerLevelAndJoinMembership = { ...defaultMember, powerLevel: 0, membership: "join" };
 | 
						|
 | 
						|
        renderComponent({
 | 
						|
            member: defaultMemberWithPowerLevelAndJoinMembership,
 | 
						|
            powerLevels: { events: { "m.room.power_levels": 1 } },
 | 
						|
        });
 | 
						|
 | 
						|
        expect(screen.getByText(/mute/i)).toBeInTheDocument();
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("disambiguateDevices", () => {
 | 
						|
    it("does not add ambiguous key to unique names", () => {
 | 
						|
        const initialDevices = [
 | 
						|
            { deviceId: "id1", getDisplayName: () => "name1" },
 | 
						|
            { deviceId: "id2", getDisplayName: () => "name2" },
 | 
						|
            { deviceId: "id3", getDisplayName: () => "name3" },
 | 
						|
        ];
 | 
						|
        disambiguateDevices(initialDevices);
 | 
						|
 | 
						|
        // mutates input so assert against initialDevices
 | 
						|
        initialDevices.forEach((device) => {
 | 
						|
            expect(device).not.toHaveProperty("ambiguous");
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    it("adds ambiguous key to all ids with non-unique names", () => {
 | 
						|
        const uniqueNameDevices = [
 | 
						|
            { deviceId: "id3", getDisplayName: () => "name3" },
 | 
						|
            { deviceId: "id4", getDisplayName: () => "name4" },
 | 
						|
            { deviceId: "id6", getDisplayName: () => "name6" },
 | 
						|
        ];
 | 
						|
        const nonUniqueNameDevices = [
 | 
						|
            { deviceId: "id1", getDisplayName: () => "nonUnique" },
 | 
						|
            { deviceId: "id2", getDisplayName: () => "nonUnique" },
 | 
						|
            { deviceId: "id5", getDisplayName: () => "nonUnique" },
 | 
						|
        ];
 | 
						|
        const initialDevices = [...uniqueNameDevices, ...nonUniqueNameDevices];
 | 
						|
        disambiguateDevices(initialDevices);
 | 
						|
 | 
						|
        // mutates input so assert against initialDevices
 | 
						|
        uniqueNameDevices.forEach((device) => {
 | 
						|
            expect(device).not.toHaveProperty("ambiguous");
 | 
						|
        });
 | 
						|
        nonUniqueNameDevices.forEach((device) => {
 | 
						|
            expect(device).toHaveProperty("ambiguous", true);
 | 
						|
        });
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("isMuted", () => {
 | 
						|
    // this member has a power level of 0
 | 
						|
    const isMutedMember = new RoomMember(mockRoom.roomId, defaultUserId);
 | 
						|
 | 
						|
    it("returns false if either argument is falsy", () => {
 | 
						|
        // @ts-ignore to let us purposely pass incorrect args
 | 
						|
        expect(isMuted(isMutedMember, null)).toBe(false);
 | 
						|
        // @ts-ignore to let us purposely pass incorrect args
 | 
						|
        expect(isMuted(null, {})).toBe(false);
 | 
						|
    });
 | 
						|
 | 
						|
    it("when powerLevelContent.events and .events_default are undefined, returns false", () => {
 | 
						|
        const powerLevelContents = {};
 | 
						|
        expect(isMuted(isMutedMember, powerLevelContents)).toBe(false);
 | 
						|
    });
 | 
						|
 | 
						|
    it("when powerLevelContent.events is undefined, uses .events_default", () => {
 | 
						|
        const higherPowerLevelContents = { events_default: 10 };
 | 
						|
        expect(isMuted(isMutedMember, higherPowerLevelContents)).toBe(true);
 | 
						|
 | 
						|
        const lowerPowerLevelContents = { events_default: -10 };
 | 
						|
        expect(isMuted(isMutedMember, lowerPowerLevelContents)).toBe(false);
 | 
						|
    });
 | 
						|
 | 
						|
    it("when powerLevelContent.events is defined but '.m.room.message' isn't, uses .events_default", () => {
 | 
						|
        const higherPowerLevelContents = { events: {}, events_default: 10 };
 | 
						|
        expect(isMuted(isMutedMember, higherPowerLevelContents)).toBe(true);
 | 
						|
 | 
						|
        const lowerPowerLevelContents = { events: {}, events_default: -10 };
 | 
						|
        expect(isMuted(isMutedMember, lowerPowerLevelContents)).toBe(false);
 | 
						|
    });
 | 
						|
 | 
						|
    it("when powerLevelContent.events and '.m.room.message' are defined, uses the value", () => {
 | 
						|
        const higherPowerLevelContents = { events: { "m.room.message": -10 }, events_default: 10 };
 | 
						|
        expect(isMuted(isMutedMember, higherPowerLevelContents)).toBe(false);
 | 
						|
 | 
						|
        const lowerPowerLevelContents = { events: { "m.room.message": 10 }, events_default: -10 };
 | 
						|
        expect(isMuted(isMutedMember, lowerPowerLevelContents)).toBe(true);
 | 
						|
    });
 | 
						|
});
 | 
						|
 | 
						|
describe("getPowerLevels", () => {
 | 
						|
    it("returns an empty object when room.currentState.getStateEvents return null", () => {
 | 
						|
        mockRoom.currentState.getStateEvents.mockReturnValueOnce(null);
 | 
						|
        expect(getPowerLevels(mockRoom)).toEqual({});
 | 
						|
    });
 | 
						|
});
 |