Mvvm split user info, create userinfoadmintools container component (#29808)

* feat: mvvm split user info, create userinfoadmintools container component

* test: mvvm userinfoadmintools and view

* feat: user info admin components more split and comments

* test: mvvm user admin info mute view models more coverage

* chore: rename user-info folder to user_info
This commit is contained in:
Marc
2025-06-13 09:08:29 +02:00
committed by GitHub
parent d89afe83a8
commit 0f0f904cb0
14 changed files with 2024 additions and 901 deletions

View File

@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { fireEvent, render, screen, cleanup, act, waitForElementToBeRemoved, waitFor } from "jest-matrix-react";
import { fireEvent, render, screen, act, waitForElementToBeRemoved, waitFor } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { type Mocked, mocked } from "jest-mock";
import {
@@ -19,7 +19,6 @@ import {
EventType,
Device,
} from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { EventEmitter } from "events";
import {
UserVerificationStatus,
@@ -30,13 +29,9 @@ import {
} from "matrix-js-sdk/src/crypto-api";
import UserInfo, {
BanToggleButton,
disambiguateDevices,
getPowerLevels,
isMuted,
PowerLevelEditor,
RoomAdminToolsContainer,
RoomKickButton,
UserInfoHeader,
UserOptionsSection,
} from "../../../../../src/components/views/right_panel/UserInfo";
@@ -53,7 +48,6 @@ import { shouldShowComponent } from "../../../../../src/customisations/helpers/U
import { UIComponent } from "../../../../../src/settings/UIFeature";
import { Action } from "../../../../../src/dispatcher/actions";
import { ShareDialog } from "../../../../../src/components/views/dialogs/ShareDialog";
import BulkRedactDialog from "../../../../../src/components/views/dialogs/BulkRedactDialog";
jest.mock("../../../../../src/utils/direct-messages", () => ({
...jest.requireActual("../../../../../src/utils/direct-messages"),
@@ -92,7 +86,6 @@ const defaultUserId = "@user:example.com";
const defaultUser = new User(defaultUserId);
let mockRoom: Mocked<Room>;
let mockSpace: Mocked<Room>;
let mockClient: Mocked<MatrixClient>;
let mockCrypto: Mocked<CryptoApi>;
const origDate = global.Date.prototype.toLocaleString;
@@ -115,23 +108,6 @@ beforeEach(() => {
getEventReadUpTo: jest.fn(),
} as unknown as Room);
mockSpace = mocked({
roomId: defaultRoomId,
getType: jest.fn().mockReturnValue("m.space"),
isSpaceRoom: jest.fn().mockReturnValue(true),
getMember: jest.fn().mockReturnValue(undefined),
getMxcAvatarUrl: jest.fn().mockReturnValue("mock-avatar-url"),
name: "test room",
on: jest.fn(),
off: jest.fn(),
currentState: {
getStateEvents: jest.fn(),
on: jest.fn(),
off: jest.fn(),
},
getEventReadUpTo: jest.fn(),
} as unknown as Room);
mockCrypto = mocked({
getDeviceVerificationStatus: jest.fn(),
getUserDeviceInfo: jest.fn(),
@@ -800,384 +776,6 @@ describe("<PowerLevelEditor />", () => {
});
});
describe("<RoomKickButton />", () => {
const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
const memberWithInviteMembership = { ...defaultMember, membership: KnownMembership.Invite };
const memberWithJoinMembership = { ...defaultMember, membership: KnownMembership.Join };
let defaultProps: Parameters<typeof RoomKickButton>[0];
beforeEach(() => {
defaultProps = {
room: mockRoom,
member: defaultMember,
startUpdating: jest.fn(),
stopUpdating: jest.fn(),
isUpdating: false,
};
});
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({ room: mockSpace, 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) }),
"mx_ConfirmSpaceUserActionDialog_wrapper",
);
// 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: KnownMembership.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(false);
expect(callback(mockRoom)).toBe(true);
});
});
describe("<BanToggleButton />", () => {
const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
const memberWithBanMembership = { ...defaultMember, membership: KnownMembership.Ban };
let defaultProps: Parameters<typeof BanToggleButton>[0];
beforeEach(() => {
defaultProps = {
room: mockRoom,
member: defaultMember,
startUpdating: jest.fn(),
stopUpdating: jest.fn(),
isUpdating: false,
};
});
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({ room: mockSpace });
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) }),
"mx_ConfirmSpaceUserActionDialog_wrapper",
);
// 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(false);
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({ room: mockSpace, 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) }),
"mx_ConfirmSpaceUserActionDialog_wrapper",
);
// 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: KnownMembership.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(false);
expect(callback(mockRoom)).toBe(true);
});
});
describe("<RoomAdminToolsContainer />", () => {
const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
defaultMember.membership = KnownMembership.Invite;
let defaultProps: Parameters<typeof RoomAdminToolsContainer>[0];
beforeEach(() => {
defaultProps = {
room: mockRoom,
member: defaultMember,
isUpdating: false,
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("button", { name: "Disinvite from room" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Ban from room" })).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Remove messages" })).toBeInTheDocument();
});
it("should show BulkRedactDialog upon clicking the Remove messages button", async () => {
const spy = jest.spyOn(Modal, "createDialog");
mockClient.getRoom.mockReturnValue(mockRoom);
mockClient.getUserId.mockReturnValue("@arbitraryId:server");
const mockMeMember = new RoomMember(mockRoom.roomId, mockClient.getUserId()!);
mockMeMember.powerLevel = 51; // defaults to 50
const defaultMemberWithPowerLevel = { ...defaultMember, powerLevel: 0 } as RoomMember;
mockRoom.getMember.mockImplementation((userId) =>
userId === mockClient.getUserId() ? mockMeMember : defaultMemberWithPowerLevel,
);
renderComponent({ member: defaultMemberWithPowerLevel });
await userEvent.click(screen.getByRole("button", { name: "Remove messages" }));
expect(spy).toHaveBeenCalledWith(
BulkRedactDialog,
expect.objectContaining({ member: defaultMemberWithPowerLevel }),
);
});
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: KnownMembership.Join,
};
renderComponent({
member: defaultMemberWithPowerLevelAndJoinMembership,
powerLevels: { events: { "m.room.power_levels": 1 } },
});
const button = screen.getByText(/mute/i);
expect(button).toBeInTheDocument();
fireEvent.click(button);
expect(defaultProps.startUpdating).toHaveBeenCalled();
});
it("should disable buttons when isUpdating=true", () => {
const mockMeMember = new RoomMember(mockRoom.roomId, "arbitraryId");
mockMeMember.powerLevel = 51; // defaults to 50
mockRoom.getMember.mockReturnValueOnce(mockMeMember);
const defaultMemberWithPowerLevelAndJoinMembership = {
...defaultMember,
powerLevel: 0,
membership: KnownMembership.Join,
};
renderComponent({
member: defaultMemberWithPowerLevelAndJoinMembership,
powerLevels: { events: { "m.room.power_levels": 1 } },
isUpdating: true,
});
const button = screen.getByRole("button", { name: "Mute" });
expect(button).toBeInTheDocument();
expect(button).toBeDisabled();
});
it("should not show mute button for one's own member", () => {
const mockMeMember = new RoomMember(mockRoom.roomId, mockClient.getSafeUserId());
mockMeMember.powerLevel = 51; // defaults to 50
mockRoom.getMember.mockReturnValueOnce(mockMeMember);
renderComponent({
member: mockMeMember,
powerLevels: { events: { "m.room.power_levels": 100 } },
});
const button = screen.queryByText(/mute/i);
expect(button).not.toBeInTheDocument();
});
});
describe("disambiguateDevices", () => {
it("does not add ambiguous key to unique names", () => {
const initialDevices = [
@@ -1217,47 +815,6 @@ describe("disambiguateDevices", () => {
});
});
describe("isMuted", () => {
// this member has a power level of 0
const isMutedMember = new RoomMember(defaultRoomId, 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);

View File

@@ -0,0 +1,306 @@
/*
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 { render, screen, fireEvent } from "jest-matrix-react";
import { type Room, type RoomMember } from "matrix-js-sdk/src/matrix";
import { mocked } from "jest-mock";
import { UserInfoAdminToolsContainer } from "../../../../../src/components/views/right_panel/user_info/UserInfoAdminToolsContainer";
import { useUserInfoAdminToolsContainerViewModel } from "../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoAdminToolsContainerViewModel";
import { useRoomKickButtonViewModel } from "../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoKickButtonViewModel";
import { useBanButtonViewModel } from "../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoBanButtonViewModel";
import { useMuteButtonViewModel } from "../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoMuteButtonViewModel";
import { useRedactMessagesButtonViewModel } from "../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoRedactButtonViewModel";
import { stubClient } from "../../../../test-utils";
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
jest.mock("../../../../../src/utils/DMRoomMap", () => {
const mock = {
getUserIdForRoomId: jest.fn(),
getDMRoomsForUserId: jest.fn(),
};
return {
shared: jest.fn().mockReturnValue(mock),
sharedInstance: mock,
};
});
jest.mock(
"../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoAdminToolsContainerViewModel",
() => ({
useUserInfoAdminToolsContainerViewModel: jest.fn().mockReturnValue({
isCurrentUserInTheRoom: true,
shouldShowKickButton: true,
shouldShowBanButton: true,
shouldShowMuteButton: true,
shouldShowRedactButton: true,
}),
}),
);
jest.mock("../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoKickButtonViewModel", () => ({
useRoomKickButtonViewModel: jest.fn().mockReturnValue({
canUserBeKicked: true,
kickLabel: "Kick",
onKickClick: jest.fn(),
}),
}));
jest.mock("../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoBanButtonViewModel", () => ({
useBanButtonViewModel: jest.fn().mockReturnValue({
banLabel: "Ban",
onBanOrUnbanClick: jest.fn(),
}),
}));
jest.mock("../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoMuteButtonViewModel", () => ({
useMuteButtonViewModel: jest.fn().mockReturnValue({
isMemberInTheRoom: true,
muteLabel: "Mute",
onMuteButtonClick: jest.fn(),
}),
}));
jest.mock("../../../../../src/components/viewmodels/right_panel/user_info/admin/UserInfoRedactButtonViewModel", () => ({
useRedactMessagesButtonViewModel: jest.fn().mockReturnValue({
onRedactAllMessagesClick: jest.fn(),
}),
}));
const defaultRoomId = "!fkfk";
describe("UserInfoAdminToolsContainer", () => {
// Setup it data
const mockRoom = mocked({
roomId: defaultRoomId,
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(),
off: jest.fn(),
currentState: {
getStateEvents: jest.fn(),
on: jest.fn(),
off: jest.fn(),
},
getEventReadUpTo: jest.fn(),
} as unknown as Room);
const mockMember = {
userId: "@user:example.com",
membership: "join",
powerLevel: 0,
} as unknown as RoomMember;
const mockPowerLevels = {
users: {
"@currentuser:example.com": 100,
},
events: {},
state_default: 50,
ban: 50,
kick: 50,
redact: 50,
};
const defaultProps = {
room: mockRoom,
member: mockMember,
powerLevels: mockPowerLevels,
isUpdating: false,
startUpdating: jest.fn(),
stopUpdating: jest.fn(),
};
const mockMatrixClient = stubClient();
const renderComponent = (props = defaultProps) => {
return render(
<MatrixClientContext.Provider value={mockMatrixClient}>
<UserInfoAdminToolsContainer {...props} />
</MatrixClientContext.Provider>,
);
};
beforeEach(() => {
mocked(useUserInfoAdminToolsContainerViewModel).mockReturnValue({
isCurrentUserInTheRoom: true,
shouldShowKickButton: true,
shouldShowBanButton: true,
shouldShowMuteButton: true,
shouldShowRedactButton: true,
});
jest.clearAllMocks();
});
it("renders all admin tools when user has permissions", () => {
renderComponent();
// Check that all buttons are rendered
expect(screen.getByText("Mute")).toBeInTheDocument();
expect(screen.getByText("Kick")).toBeInTheDocument();
expect(screen.getByText("Ban")).toBeInTheDocument();
expect(screen.getByText("Remove messages")).toBeInTheDocument();
});
it("renders no admin tools when current user is not in the room", () => {
mocked(useUserInfoAdminToolsContainerViewModel).mockReturnValue({
isCurrentUserInTheRoom: false,
shouldShowKickButton: false,
shouldShowBanButton: false,
shouldShowMuteButton: false,
shouldShowRedactButton: false,
});
const { container } = renderComponent();
// Should render an empty div
expect(container.firstChild).toBeEmptyDOMElement();
});
it("renders children when provided", () => {
render(
<UserInfoAdminToolsContainer {...defaultProps}>
<div data-testid="child-element">Custom Child</div>
</UserInfoAdminToolsContainer>,
);
expect(screen.getByTestId("child-element")).toBeInTheDocument();
expect(screen.getByText("Custom Child")).toBeInTheDocument();
});
describe("Kick behavior", () => {
it("clicking kick button calls the appropriate handler", () => {
const mockedOnKickClick = jest.fn();
mocked(useRoomKickButtonViewModel).mockReturnValue({
canUserBeKicked: true,
kickLabel: "Kick",
onKickClick: mockedOnKickClick,
});
renderComponent();
const kickButton = screen.getByText("Kick");
fireEvent.click(kickButton);
expect(mockedOnKickClick).toHaveBeenCalled();
});
it("should not display kick buttun if user can't be kicked", () => {
mocked(useRoomKickButtonViewModel).mockReturnValue({
canUserBeKicked: false,
kickLabel: "Kick",
onKickClick: jest.fn(),
});
renderComponent();
expect(screen.queryByText("Kick")).not.toBeInTheDocument();
});
it("should display the correct label when user can be disinvited", () => {
mocked(useRoomKickButtonViewModel).mockReturnValue({
canUserBeKicked: true,
kickLabel: "Disinvite",
onKickClick: jest.fn(),
});
renderComponent({
...defaultProps,
member: mockMember,
});
expect(screen.getByText("Disinvite")).toBeInTheDocument();
});
});
describe("Ban behavior", () => {
it("clicking ban button calls the appropriate handler", () => {
const mockedOnBanOrUnbanClick = jest.fn();
mocked(useBanButtonViewModel).mockReturnValue({
banLabel: "Ban",
onBanOrUnbanClick: mockedOnBanOrUnbanClick,
});
renderComponent();
const banButton = screen.getByText("Ban");
fireEvent.click(banButton);
expect(mockedOnBanOrUnbanClick).toHaveBeenCalled();
});
it("should display the correct label", () => {
const mockedOnBanOrUnbanClick = jest.fn();
mocked(useBanButtonViewModel).mockReturnValue({
banLabel: "Unban",
onBanOrUnbanClick: mockedOnBanOrUnbanClick,
});
renderComponent();
// The label should be "Unban"
expect(screen.getByText("Unban")).toBeInTheDocument();
});
});
describe("Mute behavior", () => {
it("clicking mute button calls the appropriate handler", () => {
const mockedOnMuteButtonClick = jest.fn();
mocked(useMuteButtonViewModel).mockReturnValue({
isMemberInTheRoom: true,
muteLabel: "Mute",
onMuteButtonClick: mockedOnMuteButtonClick,
});
renderComponent();
const muteButton = screen.getByText("Mute");
fireEvent.click(muteButton);
expect(mockedOnMuteButtonClick).toHaveBeenCalled();
});
it("should not display mute button if user is not in the room", () => {
mocked(useMuteButtonViewModel).mockReturnValue({
isMemberInTheRoom: false,
muteLabel: "Mute",
onMuteButtonClick: jest.fn(),
});
renderComponent();
expect(screen.queryByText("Mute")).not.toBeInTheDocument();
});
it("should display the correct label", () => {
mocked(useMuteButtonViewModel).mockReturnValue({
isMemberInTheRoom: true,
muteLabel: "Mute",
onMuteButtonClick: jest.fn(),
});
renderComponent();
expect(screen.getByText("Mute")).toBeInTheDocument();
});
});
describe("Redact behavior", () => {
it("clicking redact button calls the appropriate handler", () => {
const mockedOnRedactAllMessagesClick = jest.fn();
mocked(useRedactMessagesButtonViewModel).mockReturnValue({
onRedactAllMessagesClick: mockedOnRedactAllMessagesClick,
});
renderComponent();
const redactButton = screen.getByText("Remove messages");
fireEvent.click(redactButton);
expect(mockedOnRedactAllMessagesClick).toHaveBeenCalled();
});
});
});