Prepare for repo merge

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2024-10-15 11:35:21 +01:00
parent 0f670b8dc0
commit b084ff2313
807 changed files with 0 additions and 0 deletions
@@ -0,0 +1,164 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { fireEvent, render, RenderResult, screen } from "jest-matrix-react";
import { MatrixClient, Room, EventType, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { mocked } from "jest-mock";
import AdvancedRoomSettingsTab from "../../../../../../src/components/views/settings/tabs/room/AdvancedRoomSettingsTab";
import { mkEvent, mkStubRoom, stubClient } from "../../../../../test-utils";
import dis from "../../../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../../../src/dispatcher/actions";
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
import SettingsStore from "../../../../../../src/settings/SettingsStore";
jest.mock("../../../../../../src/dispatcher/dispatcher");
describe("AdvancedRoomSettingsTab", () => {
const roomId = "!room:example.com";
let cli: MatrixClient;
let room: Room;
const renderTab = (): RenderResult => {
return render(<AdvancedRoomSettingsTab room={room} closeSettingsFn={jest.fn()} />);
};
beforeEach(() => {
stubClient();
cli = MatrixClientPeg.safeGet();
room = mkStubRoom(roomId, "test room", cli);
mocked(cli.getRoom).mockReturnValue(room);
mocked(dis.dispatch).mockReset();
mocked(room.findPredecessor).mockImplementation((msc3946: boolean) =>
msc3946
? { roomId: "old_room_id_via_predecessor", viaServers: ["one.example.com", "two.example.com"] }
: { roomId: "old_room_id", eventId: "tombstone_event_id" },
);
});
it("should render as expected", () => {
const tab = renderTab();
expect(tab.asFragment()).toMatchSnapshot();
});
it("should display room ID", () => {
const tab = renderTab();
tab.getByText(roomId);
});
it("should display room version", () => {
mocked(room.getVersion).mockReturnValue("custom_room_version_1");
const tab = renderTab();
tab.getByText("custom_room_version_1");
});
it("displays message when room cannot federate", () => {
const createEvent = new MatrixEvent({
sender: "@a:b.com",
type: EventType.RoomCreate,
content: { "m.federate": false },
room_id: room.roomId,
state_key: "",
});
jest.spyOn(room.currentState, "getStateEvents").mockImplementation((type) =>
type === EventType.RoomCreate ? createEvent : null,
);
renderTab();
expect(screen.getByText("This room is not accessible by remote Matrix servers")).toBeInTheDocument();
});
function mockStateEvents(room: Room) {
const createEvent = mkEvent({
event: true,
user: "@a:b.com",
type: EventType.RoomCreate,
content: { predecessor: { room_id: "old_room_id", event_id: "tombstone_event_id" } },
room: room.roomId,
});
// Because we're mocking Room.findPredecessor, it may not be necessary
// to provide the actual event here, but we do need the create event,
// and in future this may be needed, so included for symmetry.
const predecessorEvent = mkEvent({
event: true,
user: "@a:b.com",
type: EventType.RoomPredecessor,
content: { predecessor_room_id: "old_room_id_via_predecessor" },
room: room.roomId,
});
type GetStateEvents2Args = (eventType: EventType | string, stateKey: string) => MatrixEvent | null;
const getStateEvents = jest.spyOn(
room.currentState,
"getStateEvents",
) as unknown as jest.MockedFunction<GetStateEvents2Args>;
getStateEvents.mockImplementation((eventType: string | null, _key: string) => {
switch (eventType) {
case EventType.RoomCreate:
return createEvent;
case EventType.RoomPredecessor:
return predecessorEvent;
default:
return null;
}
});
}
it("should link to predecessor room", async () => {
mockStateEvents(room);
const tab = renderTab();
const link = await tab.findByText("View older messages in test room.");
fireEvent.click(link);
expect(dis.dispatch).toHaveBeenCalledWith({
action: Action.ViewRoom,
event_id: "tombstone_event_id",
room_id: "old_room_id",
metricsTrigger: "WebPredecessorSettings",
metricsViaKeyboard: false,
});
});
describe("When MSC3946 support is enabled", () => {
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue")
.mockReset()
.mockImplementation((settingName) => settingName === "feature_dynamic_room_predecessors");
});
it("should link to predecessor room via MSC3946 if enabled", async () => {
mockStateEvents(room);
const tab = renderTab();
const link = await tab.findByText("View older messages in test room.");
fireEvent.click(link);
expect(dis.dispatch).toHaveBeenCalledWith({
action: Action.ViewRoom,
event_id: undefined,
room_id: "old_room_id_via_predecessor",
via_servers: ["one.example.com", "two.example.com"],
metricsTrigger: "WebPredecessorSettings",
metricsViaKeyboard: false,
});
});
it("handles when room is a space", async () => {
mockStateEvents(room);
jest.spyOn(room, "isSpaceRoom").mockReturnValue(true);
mockStateEvents(room);
const tab = renderTab();
const link = await tab.findByText("View older version of test room.");
expect(link).toBeInTheDocument();
expect(screen.getByText("Space information")).toBeInTheDocument();
});
});
});
@@ -0,0 +1,53 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render } from "jest-matrix-react";
import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import BridgeSettingsTab from "../../../../../../src/components/views/settings/tabs/room/BridgeSettingsTab";
import { getMockClientWithEventEmitter, withClientContextRenderOptions } from "../../../../../test-utils";
describe("<BridgeSettingsTab />", () => {
const userId = "@alice:server.org";
const client = getMockClientWithEventEmitter({
getRoom: jest.fn(),
});
const roomId = "!room:server.org";
const getComponent = (room: Room) =>
render(<BridgeSettingsTab room={room} />, withClientContextRenderOptions(client));
it("renders when room is not bridging messages to any platform", () => {
const room = new Room(roomId, client, userId);
const { container } = getComponent(room);
expect(container).toMatchSnapshot();
});
it("renders when room is bridging messages", () => {
const bridgeEvent = new MatrixEvent({
type: "uk.half-shot.bridge",
content: {
channel: { id: "channel-test" },
protocol: { id: "protocol-test" },
bridgebot: "test",
},
room_id: roomId,
state_key: "1",
});
const room = new Room(roomId, client, userId);
room.currentState.setStateEvents([bridgeEvent]);
client.getRoom.mockReturnValue(room);
const { container } = getComponent(room);
expect(container).toMatchSnapshot();
});
});
@@ -0,0 +1,72 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render, RenderResult, screen } from "jest-matrix-react";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import userEvent from "@testing-library/user-event";
import NotificationSettingsTab from "../../../../../../src/components/views/settings/tabs/room/NotificationSettingsTab";
import { mkStubRoom, stubClient } from "../../../../../test-utils";
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
import { EchoChamber } from "../../../../../../src/stores/local-echo/EchoChamber";
import { RoomEchoChamber } from "../../../../../../src/stores/local-echo/RoomEchoChamber";
import SettingsStore from "../../../../../../src/settings/SettingsStore";
import { SettingLevel } from "../../../../../../src/settings/SettingLevel";
describe("NotificatinSettingsTab", () => {
const roomId = "!room:example.com";
let cli: MatrixClient;
let roomProps: RoomEchoChamber;
const renderTab = (): RenderResult => {
return render(<NotificationSettingsTab roomId={roomId} closeSettingsFn={() => {}} />);
};
beforeEach(() => {
stubClient();
cli = MatrixClientPeg.safeGet();
const room = mkStubRoom(roomId, "test room", cli);
roomProps = EchoChamber.forRoom(room);
NotificationSettingsTab.contextType = React.createContext<MatrixClient>(cli);
});
it("should prevent »Settings« link click from bubbling up to radio buttons", async () => {
const tab = renderTab();
// settings link of mentions_only volume
const settingsLink = tab.container.querySelector(
"label.mx_NotificationSettingsTab_mentionsKeywordsEntry div.mx_AccessibleButton",
);
if (!settingsLink) throw new Error("settings link does not exist.");
await userEvent.click(settingsLink);
expect(roomProps.notificationVolume).not.toBe("mentions_only");
});
it("should show the currently chosen custom notification sound", async () => {
SettingsStore.setValue("notificationSound", roomId, SettingLevel.ACCOUNT, {
url: "mxc://server/custom-sound-123",
name: "custom-sound-123",
});
renderTab();
await screen.findByText("custom-sound-123");
});
it("should show the currently chosen custom notification sound url if no name", async () => {
SettingsStore.setValue("notificationSound", roomId, SettingLevel.ACCOUNT, {
url: "mxc://server/custom-sound-123",
});
renderTab();
await screen.findByText("http://this.is.a.url/server/custom-sound-123");
});
});
@@ -0,0 +1,210 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 Nordeck IT + Consulting GmbH
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import { act, fireEvent, render, screen, within } from "jest-matrix-react";
import {
EventTimeline,
EventType,
MatrixError,
MatrixEvent,
Room,
RoomMember,
RoomStateEvent,
} from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
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(
<MatrixClientContext.Provider value={client}>
<PeopleRoomSettingsTab room={room} />
</MatrixClientContext.Provider>,
);
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");
});
});
});
@@ -0,0 +1,267 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { fireEvent, getByRole, render, RenderResult, screen, waitFor } from "jest-matrix-react";
import { MatrixClient, EventType, MatrixEvent, Room, RoomMember, ISendEventResponse } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { mocked } from "jest-mock";
import { defer } from "matrix-js-sdk/src/utils";
import userEvent from "@testing-library/user-event";
import RolesRoomSettingsTab from "../../../../../../src/components/views/settings/tabs/room/RolesRoomSettingsTab";
import { mkStubRoom, withClientContextRenderOptions, stubClient } from "../../../../../test-utils";
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
import { VoiceBroadcastInfoEventType } from "../../../../../../src/voice-broadcast";
import SettingsStore from "../../../../../../src/settings/SettingsStore";
import { ElementCall } from "../../../../../../src/models/Call";
describe("RolesRoomSettingsTab", () => {
const userId = "@alice:server.org";
const roomId = "!room:example.com";
let cli: MatrixClient;
let room: Room;
const renderTab = (propRoom: Room = room): RenderResult => {
return render(<RolesRoomSettingsTab room={propRoom} />, withClientContextRenderOptions(cli));
};
const getVoiceBroadcastsSelect = (): HTMLElement => {
return renderTab().container.querySelector("select[label='Voice broadcasts']")!;
};
const getVoiceBroadcastsSelectedOption = (): HTMLElement => {
return renderTab().container.querySelector("select[label='Voice broadcasts'] option:checked")!;
};
beforeEach(() => {
stubClient();
cli = MatrixClientPeg.safeGet();
room = mkStubRoom(roomId, "test room", cli);
});
it("should allow an Admin to demote themselves but not others", () => {
mocked(cli.getRoom).mockReturnValue(room);
// @ts-ignore - mocked doesn't support overloads properly
mocked(room.currentState.getStateEvents).mockImplementation((type, key) => {
if (key === undefined) return [] as MatrixEvent[];
if (type === "m.room.power_levels") {
return new MatrixEvent({
sender: "@sender:server",
room_id: roomId,
type: "m.room.power_levels",
state_key: "",
content: {
users: {
[cli.getUserId()!]: 100,
"@admin:server": 100,
},
},
});
}
return null;
});
mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true);
const { container } = renderTab();
expect(container.querySelector(`[placeholder="${cli.getUserId()}"]`)).not.toBeDisabled();
expect(container.querySelector(`[placeholder="@admin:server"]`)).toBeDisabled();
});
it("should initially show »Moderator« permission for »Voice broadcasts«", () => {
expect(getVoiceBroadcastsSelectedOption().textContent).toBe("Moderator");
});
describe("when setting »Default« permission for »Voice broadcasts«", () => {
beforeEach(() => {
fireEvent.change(getVoiceBroadcastsSelect(), {
target: { value: 0 },
});
});
it("should update the power levels", () => {
expect(cli.sendStateEvent).toHaveBeenCalledWith(roomId, EventType.RoomPowerLevels, {
events: {
[VoiceBroadcastInfoEventType]: 0,
},
});
});
});
describe("Element Call", () => {
const setGroupCallsEnabled = (val: boolean): void => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((name: string) => {
if (name === "feature_group_calls") return val;
});
};
const getStartCallSelect = (tab: RenderResult): HTMLElement => {
return tab.container.querySelector("select[label='Start Element Call calls']")!;
};
const getStartCallSelectedOption = (tab: RenderResult): HTMLElement => {
return tab.container.querySelector("select[label='Start Element Call calls'] option:checked")!;
};
const getJoinCallSelect = (tab: RenderResult): HTMLElement => {
return tab.container.querySelector("select[label='Join Element Call calls']")!;
};
const getJoinCallSelectedOption = (tab: RenderResult): HTMLElement => {
return tab.container.querySelector("select[label='Join Element Call calls'] option:checked")!;
};
describe("Element Call enabled", () => {
beforeEach(() => {
setGroupCallsEnabled(true);
});
describe("Join Element calls", () => {
it("defaults to moderator for joining calls", () => {
expect(getJoinCallSelectedOption(renderTab())?.textContent).toBe("Moderator");
});
it("can change joining calls power level", () => {
const tab = renderTab();
fireEvent.change(getJoinCallSelect(tab), {
target: { value: 0 },
});
expect(getJoinCallSelectedOption(tab)?.textContent).toBe("Default");
expect(cli.sendStateEvent).toHaveBeenCalledWith(roomId, EventType.RoomPowerLevels, {
events: {
[ElementCall.MEMBER_EVENT_TYPE.name]: 0,
},
});
});
});
describe("Start Element calls", () => {
it("defaults to moderator for starting calls", () => {
expect(getStartCallSelectedOption(renderTab())?.textContent).toBe("Moderator");
});
it("can change starting calls power level", () => {
const tab = renderTab();
fireEvent.change(getStartCallSelect(tab), {
target: { value: 0 },
});
expect(getStartCallSelectedOption(tab)?.textContent).toBe("Default");
expect(cli.sendStateEvent).toHaveBeenCalledWith(roomId, EventType.RoomPowerLevels, {
events: {
[ElementCall.CALL_EVENT_TYPE.name]: 0,
},
});
});
});
});
it("hides when group calls disabled", () => {
setGroupCallsEnabled(false);
const tab = renderTab();
expect(getStartCallSelect(tab)).toBeFalsy();
expect(getStartCallSelectedOption(tab)).toBeFalsy();
expect(getJoinCallSelect(tab)).toBeFalsy();
expect(getJoinCallSelectedOption(tab)).toBeFalsy();
});
});
describe("Banned users", () => {
it("should not render banned section when no banned users", () => {
const room = new Room(roomId, cli, userId);
renderTab(room);
expect(screen.queryByText("Banned users")).not.toBeInTheDocument();
});
it("renders banned users", () => {
const bannedMember = new RoomMember(roomId, "@bob:server.org");
bannedMember.setMembershipEvent(
new MatrixEvent({
type: EventType.RoomMember,
content: {
membership: KnownMembership.Ban,
reason: "just testing",
},
sender: userId,
}),
);
const room = new Room(roomId, cli, userId);
jest.spyOn(room, "getMembersWithMembership").mockReturnValue([bannedMember]);
renderTab(room);
expect(screen.getByText("Banned users").parentElement).toMatchSnapshot();
});
it("uses banners display name when available", () => {
const bannedMember = new RoomMember(roomId, "@bob:server.org");
const senderMember = new RoomMember(roomId, "@alice:server.org");
senderMember.name = "Alice";
bannedMember.setMembershipEvent(
new MatrixEvent({
type: EventType.RoomMember,
content: {
membership: KnownMembership.Ban,
reason: "just testing",
},
sender: userId,
}),
);
const room = new Room(roomId, cli, userId);
jest.spyOn(room, "getMembersWithMembership").mockReturnValue([bannedMember]);
jest.spyOn(room, "getMember").mockReturnValue(senderMember);
renderTab(room);
expect(screen.getByTitle("Banned by Alice")).toBeInTheDocument();
});
});
it("should roll back power level change on error", async () => {
const deferred = defer<ISendEventResponse>();
mocked(cli.sendStateEvent).mockReturnValue(deferred.promise);
mocked(cli.getRoom).mockReturnValue(room);
// @ts-ignore - mocked doesn't support overloads properly
mocked(room.currentState.getStateEvents).mockImplementation((type, key) => {
if (key === undefined) return [] as MatrixEvent[];
if (type === "m.room.power_levels") {
return new MatrixEvent({
sender: "@sender:server",
room_id: roomId,
type: "m.room.power_levels",
state_key: "",
content: {
users: {
[cli.getUserId()!]: 100,
},
},
});
}
return null;
});
mocked(room.currentState.mayClientSendStateEvent).mockReturnValue(true);
const { container } = renderTab();
const selector = container.querySelector(`[placeholder="${cli.getUserId()}"]`)!;
fireEvent.change(selector, { target: { value: "50" } });
expect(selector).toHaveValue("50");
// Get the apply button of the privileged user section and click on it
const privilegedUsersSection = screen.getByRole("group", { name: "Privileged Users" });
const applyButton = getByRole(privilegedUsersSection, "button", { name: "Apply" });
await userEvent.click(applyButton);
deferred.reject("Error");
await waitFor(() => expect(selector).toHaveValue("100"));
});
});
@@ -0,0 +1,440 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { fireEvent, render, screen, waitFor, within } from "jest-matrix-react";
import { EventType, GuestAccess, HistoryVisibility, JoinRule, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import SecurityRoomSettingsTab from "../../../../../../src/components/views/settings/tabs/room/SecurityRoomSettingsTab";
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
import SettingsStore from "../../../../../../src/settings/SettingsStore";
import {
clearAllModals,
flushPromises,
getMockClientWithEventEmitter,
mockClientMethodsUser,
} from "../../../../../test-utils";
import { filterBoolean } from "../../../../../../src/utils/arrays";
describe("<SecurityRoomSettingsTab />", () => {
const userId = "@alice:server.org";
const client = getMockClientWithEventEmitter({
...mockClientMethodsUser(userId),
getRoom: jest.fn(),
isRoomEncrypted: jest.fn(),
getLocalAliases: jest.fn().mockReturnValue([]),
sendStateEvent: jest.fn(),
getClientWellKnown: jest.fn(),
});
const roomId = "!room:server.org";
const getComponent = (room: Room, closeSettingsFn = jest.fn()) =>
render(<SecurityRoomSettingsTab room={room} closeSettingsFn={closeSettingsFn} />, {
wrapper: ({ children }) => (
<MatrixClientContext.Provider value={client}>{children}</MatrixClientContext.Provider>
),
});
const setRoomStateEvents = (
room: Room,
joinRule?: JoinRule,
guestAccess?: GuestAccess,
history?: HistoryVisibility,
): void => {
const events = filterBoolean<MatrixEvent>([
new MatrixEvent({
type: EventType.RoomCreate,
content: { version: "test" },
sender: userId,
state_key: "",
room_id: room.roomId,
}),
guestAccess &&
new MatrixEvent({
type: EventType.RoomGuestAccess,
content: { guest_access: guestAccess },
sender: userId,
state_key: "",
room_id: room.roomId,
}),
history &&
new MatrixEvent({
type: EventType.RoomHistoryVisibility,
content: { history_visibility: history },
sender: userId,
state_key: "",
room_id: room.roomId,
}),
joinRule &&
new MatrixEvent({
type: EventType.RoomJoinRules,
content: { join_rule: joinRule },
sender: userId,
state_key: "",
room_id: room.roomId,
}),
]);
room.currentState.setStateEvents(events);
};
beforeEach(async () => {
client.sendStateEvent.mockReset().mockResolvedValue({ event_id: "test" });
client.isRoomEncrypted.mockReturnValue(false);
client.getClientWellKnown.mockReturnValue(undefined);
jest.spyOn(SettingsStore, "getValue").mockRestore();
await clearAllModals();
});
describe("join rule", () => {
it("warns when trying to make an encrypted room public", async () => {
const room = new Room(roomId, client, userId);
client.isRoomEncrypted.mockReturnValue(true);
setRoomStateEvents(room, JoinRule.Invite);
getComponent(room);
fireEvent.click(screen.getByLabelText("Public"));
const modal = await screen.findByRole("dialog");
expect(modal).toMatchSnapshot();
fireEvent.click(screen.getByText("Cancel"));
// join rule not updated
expect(screen.getByLabelText("Private (invite only)").hasAttribute("checked")).toBeTruthy();
});
it("updates join rule", async () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, JoinRule.Invite);
getComponent(room);
fireEvent.click(screen.getByLabelText("Public"));
await flushPromises();
expect(client.sendStateEvent).toHaveBeenCalledWith(
room.roomId,
EventType.RoomJoinRules,
{
join_rule: JoinRule.Public,
},
"",
);
});
it("handles error when updating join rule fails", async () => {
const room = new Room(roomId, client, userId);
client.sendStateEvent.mockRejectedValue("oups");
setRoomStateEvents(room, JoinRule.Invite);
getComponent(room);
fireEvent.click(screen.getByLabelText("Public"));
await flushPromises();
const dialog = await screen.findByRole("dialog");
expect(dialog).toMatchSnapshot();
fireEvent.click(within(dialog).getByText("OK"));
});
it("displays advanced section toggle when join rule is public", () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, JoinRule.Public);
getComponent(room);
expect(screen.getByText("Show advanced")).toBeInTheDocument();
});
it("does not display advanced section toggle when join rule is not public", () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, JoinRule.Invite);
getComponent(room);
expect(screen.queryByText("Show advanced")).not.toBeInTheDocument();
});
});
describe("guest access", () => {
it("uses forbidden by default when room has no guest access event", () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, JoinRule.Public);
getComponent(room);
fireEvent.click(screen.getByText("Show advanced"));
expect(screen.getByLabelText("Enable guest access").getAttribute("aria-checked")).toBe("false");
});
it("updates guest access on toggle", () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, JoinRule.Public);
getComponent(room);
fireEvent.click(screen.getByText("Show advanced"));
fireEvent.click(screen.getByLabelText("Enable guest access"));
// toggle set immediately
expect(screen.getByLabelText("Enable guest access").getAttribute("aria-checked")).toBe("true");
expect(client.sendStateEvent).toHaveBeenCalledWith(
room.roomId,
EventType.RoomGuestAccess,
{ guest_access: GuestAccess.CanJoin },
"",
);
});
it("logs error and resets state when updating guest access fails", async () => {
client.sendStateEvent.mockRejectedValue("oups");
jest.spyOn(logger, "error").mockImplementation(() => {});
const room = new Room(roomId, client, userId);
setRoomStateEvents(room, JoinRule.Public, GuestAccess.CanJoin);
getComponent(room);
fireEvent.click(screen.getByText("Show advanced"));
fireEvent.click(screen.getByLabelText("Enable guest access"));
// toggle set immediately
expect(screen.getByLabelText("Enable guest access").getAttribute("aria-checked")).toBe("false");
await flushPromises();
expect(client.sendStateEvent).toHaveBeenCalled();
expect(logger.error).toHaveBeenCalledWith("oups");
// toggle reset to old value
expect(screen.getByLabelText("Enable guest access").getAttribute("aria-checked")).toBe("true");
});
});
describe("history visibility", () => {
it("does not render section when RoomHistorySettings feature is disabled", () => {
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
const room = new Room(roomId, client, userId);
setRoomStateEvents(room);
getComponent(room);
expect(screen.queryByText("Who can read history")).not.toBeInTheDocument();
});
it("uses shared as default history visibility when no state event found", () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room);
getComponent(room);
expect(screen.getByText("Who can read history?").parentElement).toMatchSnapshot();
expect(screen.getByDisplayValue(HistoryVisibility.Shared)).toBeChecked();
});
it("does not render world readable option when room is encrypted", () => {
const room = new Room(roomId, client, userId);
client.isRoomEncrypted.mockReturnValue(true);
setRoomStateEvents(room);
getComponent(room);
expect(screen.queryByDisplayValue(HistoryVisibility.WorldReadable)).not.toBeInTheDocument();
});
it("renders world readable option when room is encrypted and history is already set to world readable", () => {
const room = new Room(roomId, client, userId);
client.isRoomEncrypted.mockReturnValue(true);
setRoomStateEvents(room, undefined, undefined, HistoryVisibility.WorldReadable);
getComponent(room);
expect(screen.getByDisplayValue(HistoryVisibility.WorldReadable)).toBeInTheDocument();
});
it("updates history visibility", () => {
const room = new Room(roomId, client, userId);
getComponent(room);
fireEvent.click(screen.getByDisplayValue(HistoryVisibility.Invited));
// toggle updated immediately
expect(screen.getByDisplayValue(HistoryVisibility.Invited)).toBeChecked();
expect(client.sendStateEvent).toHaveBeenCalledWith(
room.roomId,
EventType.RoomHistoryVisibility,
{
history_visibility: HistoryVisibility.Invited,
},
"",
);
});
it("handles error when updating history visibility", async () => {
const room = new Room(roomId, client, userId);
client.sendStateEvent.mockRejectedValue("oups");
jest.spyOn(logger, "error").mockImplementation(() => {});
getComponent(room);
fireEvent.click(screen.getByDisplayValue(HistoryVisibility.Invited));
// toggle updated immediately
expect(screen.getByDisplayValue(HistoryVisibility.Invited)).toBeChecked();
await flushPromises();
// reset to before updated value
expect(screen.getByDisplayValue(HistoryVisibility.Shared)).toBeChecked();
expect(logger.error).toHaveBeenCalledWith("oups");
});
});
describe("encryption", () => {
it("displays encryption as enabled", () => {
const room = new Room(roomId, client, userId);
client.isRoomEncrypted.mockReturnValue(true);
setRoomStateEvents(room);
getComponent(room);
expect(screen.getByLabelText("Encrypted")).toBeChecked();
// can't disable encryption once enabled
expect(screen.getByLabelText("Encrypted").getAttribute("aria-disabled")).toEqual("true");
});
it("asks users to confirm when setting room to encrypted", async () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room);
getComponent(room);
expect(screen.getByLabelText("Encrypted")).not.toBeChecked();
fireEvent.click(screen.getByLabelText("Encrypted"));
const dialog = await screen.findByRole("dialog");
fireEvent.click(within(dialog).getByText("Cancel"));
expect(client.sendStateEvent).not.toHaveBeenCalled();
expect(screen.getByLabelText("Encrypted")).not.toBeChecked();
});
it("enables encryption after confirmation", async () => {
const room = new Room(roomId, client, userId);
setRoomStateEvents(room);
getComponent(room);
expect(screen.getByLabelText("Encrypted")).not.toBeChecked();
fireEvent.click(screen.getByLabelText("Encrypted"));
const dialog = await screen.findByRole("dialog");
expect(within(dialog).getByText("Enable encryption?")).toBeInTheDocument();
fireEvent.click(within(dialog).getByText("OK"));
await waitFor(() =>
expect(client.sendStateEvent).toHaveBeenCalledWith(room.roomId, EventType.RoomEncryption, {
algorithm: "m.megolm.v1.aes-sha2",
}),
);
});
it("renders world readable option when room is encrypted and history is already set to world readable", () => {
const room = new Room(roomId, client, userId);
client.isRoomEncrypted.mockReturnValue(true);
setRoomStateEvents(room, undefined, undefined, HistoryVisibility.WorldReadable);
getComponent(room);
expect(screen.getByDisplayValue(HistoryVisibility.WorldReadable)).toBeInTheDocument();
});
it("updates history visibility", () => {
const room = new Room(roomId, client, userId);
getComponent(room);
fireEvent.click(screen.getByDisplayValue(HistoryVisibility.Invited));
// toggle updated immediately
expect(screen.getByDisplayValue(HistoryVisibility.Invited)).toBeChecked();
expect(client.sendStateEvent).toHaveBeenCalledWith(
room.roomId,
EventType.RoomHistoryVisibility,
{
history_visibility: HistoryVisibility.Invited,
},
"",
);
});
it("handles error when updating history visibility", async () => {
const room = new Room(roomId, client, userId);
client.sendStateEvent.mockRejectedValue("oups");
jest.spyOn(logger, "error").mockImplementation(() => {});
getComponent(room);
fireEvent.click(screen.getByDisplayValue(HistoryVisibility.Invited));
// toggle updated immediately
expect(screen.getByDisplayValue(HistoryVisibility.Invited)).toBeChecked();
await flushPromises();
// reset to before updated value
expect(screen.getByDisplayValue(HistoryVisibility.Shared)).toBeChecked();
expect(logger.error).toHaveBeenCalledWith("oups");
});
describe("when encryption is force disabled by e2ee well-known config", () => {
beforeEach(() => {
client.getClientWellKnown.mockReturnValue({
"io.element.e2ee": {
force_disable: true,
},
});
});
it("displays encrypted rooms as encrypted", () => {
// rooms that are already encrypted still show encrypted
const room = new Room(roomId, client, userId);
client.isRoomEncrypted.mockReturnValue(true);
setRoomStateEvents(room);
getComponent(room);
expect(screen.getByLabelText("Encrypted")).toBeChecked();
expect(screen.getByLabelText("Encrypted").getAttribute("aria-disabled")).toEqual("true");
expect(screen.getByText("Once enabled, encryption cannot be disabled.")).toBeInTheDocument();
});
it("displays unencrypted rooms with toggle disabled", () => {
const room = new Room(roomId, client, userId);
client.isRoomEncrypted.mockReturnValue(false);
setRoomStateEvents(room);
getComponent(room);
expect(screen.getByLabelText("Encrypted")).not.toBeChecked();
expect(screen.getByLabelText("Encrypted").getAttribute("aria-disabled")).toEqual("true");
expect(screen.queryByText("Once enabled, encryption cannot be disabled.")).not.toBeInTheDocument();
expect(screen.getByText("Your server requires encryption to be disabled.")).toBeInTheDocument();
});
});
});
});
@@ -0,0 +1,135 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { fireEvent, render, RenderResult, waitFor } from "jest-matrix-react";
import { MatrixClient, Room, MatrixEvent, EventType, JoinRule } from "matrix-js-sdk/src/matrix";
import { mkStubRoom, stubClient } from "../../../../../test-utils";
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
import { VoipRoomSettingsTab } from "../../../../../../src/components/views/settings/tabs/room/VoipRoomSettingsTab";
import { ElementCall } from "../../../../../../src/models/Call";
describe("VoipRoomSettingsTab", () => {
const roomId = "!room:example.com";
let cli: MatrixClient;
let room: Room;
const renderTab = (): RenderResult => {
return render(<VoipRoomSettingsTab room={room} />);
};
beforeEach(() => {
stubClient();
cli = MatrixClientPeg.safeGet();
room = mkStubRoom(roomId, "test room", cli);
jest.spyOn(cli, "sendStateEvent");
jest.spyOn(cli, "getRoom").mockReturnValue(room);
});
describe("Element Call", () => {
const mockPowerLevels = (events: Record<string, number>): void => {
jest.spyOn(room.currentState, "getStateEvents").mockReturnValue({
getContent: () => ({
events,
}),
} as unknown as MatrixEvent);
};
const getElementCallSwitch = (tab: RenderResult): HTMLElement => {
return tab.container.querySelector("[data-testid='element-call-switch']")!;
};
describe("correct state", () => {
it("shows enabled when call member power level is 0", () => {
mockPowerLevels({ [ElementCall.MEMBER_EVENT_TYPE.name]: 0 });
const tab = renderTab();
expect(getElementCallSwitch(tab).querySelector("[aria-checked='true']")).toBeTruthy();
});
it.each([1, 50, 100])("shows disabled when call member power level is 0", (level: number) => {
mockPowerLevels({ [ElementCall.MEMBER_EVENT_TYPE.name]: level });
const tab = renderTab();
expect(getElementCallSwitch(tab).querySelector("[aria-checked='false']")).toBeTruthy();
});
});
describe("enabling/disabling", () => {
describe("enabling Element calls", () => {
beforeEach(() => {
mockPowerLevels({ [ElementCall.MEMBER_EVENT_TYPE.name]: 100 });
});
it("enables Element calls in public room", async () => {
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Public);
const tab = renderTab();
fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch")!);
await waitFor(() =>
expect(cli.sendStateEvent).toHaveBeenCalledWith(
room.roomId,
EventType.RoomPowerLevels,
expect.objectContaining({
events: {
[ElementCall.CALL_EVENT_TYPE.name]: 50,
[ElementCall.MEMBER_EVENT_TYPE.name]: 0,
},
}),
),
);
});
it("enables Element calls in private room", async () => {
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Invite);
const tab = renderTab();
fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch")!);
await waitFor(() =>
expect(cli.sendStateEvent).toHaveBeenCalledWith(
room.roomId,
EventType.RoomPowerLevels,
expect.objectContaining({
events: {
[ElementCall.CALL_EVENT_TYPE.name]: 0,
[ElementCall.MEMBER_EVENT_TYPE.name]: 0,
},
}),
),
);
});
});
it("disables Element calls", async () => {
mockPowerLevels({ [ElementCall.MEMBER_EVENT_TYPE.name]: 0 });
const tab = renderTab();
fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch")!);
await waitFor(() =>
expect(cli.sendStateEvent).toHaveBeenCalledWith(
room.roomId,
EventType.RoomPowerLevels,
expect.objectContaining({
events: {
[ElementCall.CALL_EVENT_TYPE.name]: 100,
[ElementCall.MEMBER_EVENT_TYPE.name]: 100,
},
}),
),
);
});
});
});
});
@@ -0,0 +1,83 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AdvancedRoomSettingsTab should render as expected 1`] = `
<DocumentFragment>
<div
class="mx_SettingsTab"
>
<div
class="mx_SettingsTab_sections"
>
<div
class="mx_SettingsSection"
>
<h2
class="mx_Heading_h3"
>
Advanced
</h2>
<div
class="mx_SettingsSection_subSections"
>
<div
class="mx_SettingsSubsection"
>
<div
class="mx_SettingsSubsectionHeading"
>
<h3
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
>
Room information
</h3>
</div>
<div
class="mx_SettingsSubsection_content"
>
<div>
<span>
Internal room ID
</span>
<div
class="mx_CopyableText mx_CopyableText_border"
>
!room:example.com
<div
aria-label="Copy"
class="mx_AccessibleButton mx_CopyableText_copyButton"
role="button"
tabindex="0"
/>
</div>
</div>
</div>
</div>
<div
class="mx_SettingsSubsection"
>
<div
class="mx_SettingsSubsectionHeading"
>
<h3
class="mx_Heading_h4 mx_SettingsSubsectionHeading_heading"
>
Room version
</h3>
</div>
<div
class="mx_SettingsSubsection_content"
>
<div>
<span>
Room version:
</span>
 1
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</DocumentFragment>
`;
@@ -0,0 +1,128 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<BridgeSettingsTab /> renders when room is bridging messages 1`] = `
<div>
<div
class="mx_SettingsTab"
>
<div
class="mx_SettingsTab_sections"
>
<div
class="mx_SettingsSection"
>
<h2
class="mx_Heading_h3"
>
Bridges
</h2>
<div
class="mx_SettingsSection_subSections"
>
<div>
<p>
<span>
This room is bridging messages to the following platforms.
<a
href="https://matrix.org/bridges/"
rel="noreferrer noopener"
target="_blank"
>
Learn more.
</a>
</span>
</p>
<ul
class="mx_RoomSettingsDialog_BridgeList"
>
<li
class="mx_RoomSettingsDialog_BridgeList_listItem"
>
<div
class="mx_RoomSettingsDialog_column_icon"
>
<div
class="mx_RoomSettingsDialog_noProtocolIcon"
/>
</div>
<div
class="mx_RoomSettingsDialog_column_data"
>
<h3
class="mx_RoomSettingsDialog_column_data_protocolName"
>
protocol-test
</h3>
<p
class="mx_RoomSettingsDialog_column_data_details mx_RoomSettingsDialog_workspace_channel_details"
>
<span
class="mx_RoomSettingsDialog_channel"
>
<span>
Channel:
<span>
channel-test
</span>
</span>
</span>
</p>
<ul
class="mx_RoomSettingsDialog_column_data_metadata mx_RoomSettingsDialog_metadata"
>
<li>
<span>
This bridge is managed by
.
</span>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`<BridgeSettingsTab /> renders when room is not bridging messages to any platform 1`] = `
<div>
<div
class="mx_SettingsTab"
>
<div
class="mx_SettingsTab_sections"
>
<div
class="mx_SettingsSection"
>
<h2
class="mx_Heading_h3"
>
Bridges
</h2>
<div
class="mx_SettingsSection_subSections"
>
<p>
<span>
This room isn't bridging messages to any platforms.
<a
href="https://matrix.org/bridges/"
rel="noreferrer noopener"
target="_blank"
>
Learn more.
</a>
</span>
</p>
</div>
</div>
</div>
</div>
</div>
`;
@@ -0,0 +1,175 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PeopleRoomSettingsTab with requests to join renders requests fully 1`] = `
<fieldset
class="mx_SettingsFieldset"
>
<legend
class="mx_SettingsFieldset_legend"
>
Asking to join
</legend>
<div
class="mx_SettingsFieldset_content"
>
<div
class="mx_PeopleRoomSettingsTab_knock"
>
<span
aria-label="Profile picture"
class="_avatar_mcap2_17 mx_BaseAvatar mx_PeopleRoomSettingsTab_avatar"
data-color="4"
data-testid="avatar-img"
data-type="round"
style="--cpd-avatar-size: 42px;"
title="@albert.einstein:example.org"
>
<img
alt=""
class="_image_mcap2_50"
data-type="round"
height="42px"
loading="lazy"
referrerpolicy="no-referrer"
src="mxc://example.org/albert-einstein.png"
width="42px"
/>
</span>
<div
class="mx_PeopleRoomSettingsTab_content"
>
<span
class="mx_PeopleRoomSettingsTab_name"
>
Albert Einstein
</span>
<time
class="mx_PeopleRoomSettingsTab_timestamp"
>
Apr 18, 1955
</time>
<span
class="mx_PeopleRoomSettingsTab_userId"
>
@albert.einstein:example.org
</span>
<p
class="mx_PeopleRoomSettingsTab_seeMoreOrLess"
>
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…
</p>
<div
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link"
role="button"
tabindex="0"
>
See more
</div>
</div>
<div
aria-label="Deny"
class="mx_AccessibleButton mx_PeopleRoomSettingsTab_action mx_AccessibleButton_hasKind mx_AccessibleButton_kind_icon_primary_outline"
role="button"
tabindex="0"
>
<svg
fill="currentColor"
height="18"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
/>
</svg>
</div>
<div
aria-label="Approve"
class="mx_AccessibleButton mx_PeopleRoomSettingsTab_action mx_AccessibleButton_hasKind mx_AccessibleButton_kind_icon_primary"
role="button"
tabindex="0"
>
<div
height="18"
width="18"
/>
</div>
</div>
</div>
</fieldset>
`;
exports[`PeopleRoomSettingsTab with requests to join renders requests reduced 1`] = `
<fieldset
class="mx_SettingsFieldset"
>
<legend
class="mx_SettingsFieldset_legend"
>
Asking to join
</legend>
<div
class="mx_SettingsFieldset_content"
>
<div
class="mx_PeopleRoomSettingsTab_knock"
>
<span
class="_avatar_mcap2_17 mx_BaseAvatar mx_PeopleRoomSettingsTab_avatar _avatar-imageless_mcap2_61"
data-color="4"
data-testid="avatar-img"
data-type="round"
role="presentation"
style="--cpd-avatar-size: 42px;"
title="@albert.einstein:example.org"
>
a
</span>
<div
class="mx_PeopleRoomSettingsTab_content"
>
<span
class="mx_PeopleRoomSettingsTab_name"
>
albert.einstein
</span>
<span
class="mx_PeopleRoomSettingsTab_userId"
>
@albert.einstein:example.org
</span>
</div>
<div
aria-label="Deny"
class="mx_AccessibleButton mx_PeopleRoomSettingsTab_action mx_AccessibleButton_hasKind mx_AccessibleButton_kind_icon_primary_outline"
role="button"
tabindex="0"
>
<svg
fill="currentColor"
height="18"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
/>
</svg>
</div>
<div
aria-label="Approve"
class="mx_AccessibleButton mx_PeopleRoomSettingsTab_action mx_AccessibleButton_hasKind mx_AccessibleButton_kind_icon_primary"
role="button"
tabindex="0"
>
<div
height="18"
width="18"
/>
</div>
</div>
</div>
</fieldset>
`;
@@ -0,0 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`RolesRoomSettingsTab Banned users renders banned users 1`] = `
<fieldset
class="mx_SettingsFieldset"
>
<legend
class="mx_SettingsFieldset_legend"
>
Banned users
</legend>
<div
class="mx_SettingsFieldset_content"
>
<ul
class="mx_RolesRoomSettingsTab_bannedList"
>
<li>
<span
title="Banned by @alice:server.org"
>
<strong>
@bob:server.org
</strong>
Reason: just testing
</span>
</li>
</ul>
</div>
</fieldset>
`;
@@ -0,0 +1,235 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<SecurityRoomSettingsTab /> history visibility uses shared as default history visibility when no state event found 1`] = `
<fieldset
class="mx_SettingsFieldset"
>
<legend
class="mx_SettingsFieldset_legend"
>
Who can read history?
</legend>
<div
class="mx_SettingsFieldset_description"
>
<div
class="mx_SettingsSubsection_text"
>
Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.
</div>
</div>
<div
class="mx_SettingsFieldset_content"
>
<label
class="mx_StyledRadioButton mx_StyledRadioButton_enabled"
>
<input
id="historyVis-world_readable"
name="historyVis"
type="radio"
value="world_readable"
/>
<div>
<div />
</div>
<div
class="mx_StyledRadioButton_content"
>
Anyone
</div>
<div
class="mx_StyledRadioButton_spacer"
/>
</label>
<label
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
>
<input
checked=""
id="historyVis-shared"
name="historyVis"
type="radio"
value="shared"
/>
<div>
<div />
</div>
<div
class="mx_StyledRadioButton_content"
>
Members only (since the point in time of selecting this option)
</div>
<div
class="mx_StyledRadioButton_spacer"
/>
</label>
<label
class="mx_StyledRadioButton mx_StyledRadioButton_enabled"
>
<input
id="historyVis-invited"
name="historyVis"
type="radio"
value="invited"
/>
<div>
<div />
</div>
<div
class="mx_StyledRadioButton_content"
>
Members only (since they were invited)
</div>
<div
class="mx_StyledRadioButton_spacer"
/>
</label>
<label
class="mx_StyledRadioButton mx_StyledRadioButton_enabled"
>
<input
id="historyVis-joined"
name="historyVis"
type="radio"
value="joined"
/>
<div>
<div />
</div>
<div
class="mx_StyledRadioButton_content"
>
Members only (since they joined)
</div>
<div
class="mx_StyledRadioButton_spacer"
/>
</label>
</div>
</fieldset>
`;
exports[`<SecurityRoomSettingsTab /> join rule handles error when updating join rule fails 1`] = `
<div
aria-describedby="mx_Dialog_content"
aria-labelledby="mx_BaseDialog_title"
class="mx_ErrorDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
>
<div
class="mx_Dialog_header"
>
<h1
class="mx_Heading_h3 mx_Dialog_title"
id="mx_BaseDialog_title"
>
Failed to update the join rules
</h1>
</div>
<div
class="mx_Dialog_content"
id="mx_Dialog_content"
>
Unknown failure
</div>
<div
class="mx_Dialog_buttons"
>
<button
class="mx_Dialog_primary"
>
OK
</button>
</div>
<div
aria-label="Close dialog"
class="mx_AccessibleButton mx_Dialog_cancelButton"
role="button"
tabindex="0"
/>
</div>
`;
exports[`<SecurityRoomSettingsTab /> join rule warns when trying to make an encrypted room public 1`] = `
<div
aria-describedby="mx_Dialog_content"
aria-labelledby="mx_BaseDialog_title"
class="mx_QuestionDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
>
<div
class="mx_Dialog_header"
>
<h1
class="mx_Heading_h3 mx_Dialog_title"
id="mx_BaseDialog_title"
>
Are you sure you want to make this encrypted room public?
</h1>
</div>
<div
class="mx_Dialog_content"
id="mx_Dialog_content"
>
<div>
<p>
<span>
<strong>
It's not recommended to make encrypted rooms public.
</strong>
It will mean anyone can find and join the room, so anyone can read messages. You'll get none of the benefits of encryption. Encrypting messages in a public room will make receiving and sending messages slower.
</span>
</p>
<p>
<span>
To avoid these issues, create a
<div
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
role="button"
tabindex="0"
>
new public room
</div>
for the conversation you plan to have.
</span>
</p>
</div>
</div>
<div
class="mx_Dialog_buttons"
>
<span
class="mx_Dialog_buttons_row"
>
<button
data-testid="dialog-cancel-button"
type="button"
>
Cancel
</button>
<button
class="mx_Dialog_primary"
data-testid="dialog-primary-button"
type="button"
>
OK
</button>
</span>
</div>
<div
aria-label="Close dialog"
class="mx_AccessibleButton mx_Dialog_cancelButton"
role="button"
tabindex="0"
/>
</div>
`;