mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-03 06:55:44 +00:00
Prepare for repo merge
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
@@ -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,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+83
@@ -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>
|
||||
`;
|
||||
+128
@@ -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>
|
||||
`;
|
||||
+175
@@ -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>
|
||||
`;
|
||||
+32
@@ -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>
|
||||
`;
|
||||
+235
@@ -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>
|
||||
`;
|
||||
Reference in New Issue
Block a user