You've already forked element-web
mirror of
https://github.com/element-hq/element-web.git
synced 2025-08-08 03:42:14 +03:00
Implement MSC4155: Invite filtering (#29603)
* Add settings for MSC4155 * copyright * Tweak to not use js-sdk * Update for latest MSC * Various tidyups * Move tab * i18n * update .snap * mvvm * lint * add header * Remove capability check * fix * Rewrite to use Settings * lint * lint * fix test * Tweaks * lint * revert copyright * update screenshot * cleanup
This commit is contained in:
Binary file not shown.
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 272 KiB |
29
src/@types/invite-rules.ts
Normal file
29
src/@types/invite-rules.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
export const INVITE_RULES_ACCOUNT_DATA_TYPE = "org.matrix.msc4155.invite_permission_config";
|
||||
|
||||
export interface InviteConfigAccountData {
|
||||
allowed_users?: string[];
|
||||
blocked_users?: string[];
|
||||
ignored_users?: string[];
|
||||
allowed_servers?: string[];
|
||||
blocked_servers?: string[];
|
||||
ignored_servers?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed values based on MSC4155. Currently Element Web only supports
|
||||
* blocking all invites.
|
||||
*/
|
||||
export interface ComputedInviteConfig extends Record<string, unknown> {
|
||||
/**
|
||||
* Are all invites blocked. This is only about blocking all invites,
|
||||
* but this being false may still block invites through other rules.
|
||||
*/
|
||||
allBlocked: boolean;
|
||||
}
|
4
src/@types/matrix-js-sdk.d.ts
vendored
4
src/@types/matrix-js-sdk.d.ts
vendored
@@ -15,6 +15,7 @@ import type { EmptyObject } from "matrix-js-sdk/src/matrix";
|
||||
import type { DeviceClientInformation } from "../utils/device/types.ts";
|
||||
import type { UserWidget } from "../utils/WidgetUtils-types.ts";
|
||||
import { type MediaPreviewConfig } from "./media_preview.ts";
|
||||
import { type INVITE_RULES_ACCOUNT_DATA_TYPE, type InviteConfigAccountData } from "./invite-rules.ts";
|
||||
|
||||
// Extend Matrix JS SDK types via Typescript declaration merging to support unspecced event fields and types
|
||||
declare module "matrix-js-sdk/src/types" {
|
||||
@@ -60,7 +61,6 @@ declare module "matrix-js-sdk/src/types" {
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface AccountDataEvents {
|
||||
// Analytics account data event
|
||||
"im.vector.analytics": {
|
||||
@@ -89,6 +89,8 @@ declare module "matrix-js-sdk/src/types" {
|
||||
accepted: string[];
|
||||
};
|
||||
|
||||
// MSC4155: Invite filtering
|
||||
[INVITE_RULES_ACCOUNT_DATA_TYPE]: InviteConfigAccountData;
|
||||
"io.element.msc4278.media_preview_config": MediaPreviewConfig;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type FC, useCallback, useState } from "react";
|
||||
import { Root } from "@vector-im/compound-web";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { _t } from "../../../../../languageHandler";
|
||||
import { useSettingValue } from "../../../../../hooks/useSettings";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
|
||||
|
||||
export const InviteRulesAccountSetting: FC = () => {
|
||||
const rules = useSettingValue("inviteRules");
|
||||
const settingsDisabled = SettingsStore.disabledMessage("inviteRules");
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const onChange = useCallback(async (checked: boolean) => {
|
||||
try {
|
||||
setBusy(true);
|
||||
await SettingsStore.setValue("inviteRules", null, SettingLevel.ACCOUNT, {
|
||||
allBlocked: !checked,
|
||||
});
|
||||
} catch (ex) {
|
||||
logger.error(`Unable to set invite rules`, ex);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<Root className="mx_MediaPreviewAccountSetting_Form">
|
||||
<LabelledToggleSwitch
|
||||
className="mx_MediaPreviewAccountSetting_ToggleSwitch"
|
||||
label={_t("settings|invite_controls|default_label")}
|
||||
value={!rules.allBlocked}
|
||||
onChange={onChange}
|
||||
tooltip={settingsDisabled}
|
||||
disabled={!!settingsDisabled || busy}
|
||||
/>
|
||||
</Root>
|
||||
);
|
||||
};
|
@@ -33,6 +33,7 @@ import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
|
||||
import * as TimezoneHandler from "../../../../../TimezoneHandler";
|
||||
import { type BooleanSettingKey } from "../../../../../settings/Settings.tsx";
|
||||
import { MediaPreviewAccountSettings } from "./MediaPreviewAccountSettings.tsx";
|
||||
import { InviteRulesAccountSetting } from "./InviteRulesAccountSettings.tsx";
|
||||
|
||||
interface IProps {
|
||||
closeSettingsFn(success: boolean): void;
|
||||
@@ -339,6 +340,7 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
|
||||
|
||||
<SettingsSubsection heading={_t("common|moderation_and_safety")} legacy={false}>
|
||||
<MediaPreviewAccountSettings />
|
||||
<InviteRulesAccountSetting />
|
||||
</SettingsSubsection>
|
||||
|
||||
<SettingsSubsection heading={_t("settings|preferences|room_directory_heading")}>
|
||||
|
@@ -2686,6 +2686,9 @@
|
||||
"inline_url_previews_room": "Enable URL previews by default for participants in this room",
|
||||
"inline_url_previews_room_account": "Enable URL previews for this room (only affects you)",
|
||||
"insert_trailing_colon_mentions": "Insert a trailing colon after user mentions at the start of a message",
|
||||
"invite_controls": {
|
||||
"default_label": "Allow users to invite you to rooms"
|
||||
},
|
||||
"jump_to_bottom_on_send": "Jump to the bottom of the timeline when you send a message",
|
||||
"key_backup": {
|
||||
"backup_in_progress": "Your keys are being backed up (the first backup could take a few minutes).",
|
||||
@@ -2752,6 +2755,7 @@
|
||||
"show_in_private": "In private rooms",
|
||||
"show_media": "Always show"
|
||||
},
|
||||
"not_supported": "Your server does not implement this feature.",
|
||||
"notifications": {
|
||||
"default_setting_description": "This setting will be applied by default to all your rooms.",
|
||||
"default_setting_section": "I want to be notified for (Default Setting)",
|
||||
|
@@ -47,6 +47,8 @@ import { type RecentEmojiData } from "../emojipicker/recent.ts";
|
||||
import { type Assignable } from "../@types/common.ts";
|
||||
import { SortingAlgorithm } from "../stores/room-list-v3/skip-list/sorters/index.ts";
|
||||
import MediaPreviewConfigController from "./controllers/MediaPreviewConfigController.ts";
|
||||
import InviteRulesConfigController from "./controllers/InviteRulesConfigController.ts";
|
||||
import { type ComputedInviteConfig } from "../@types/invite-rules.ts";
|
||||
|
||||
export const defaultWatchManager = new WatchManager();
|
||||
|
||||
@@ -351,6 +353,7 @@ export interface Settings {
|
||||
"Electron.enableHardwareAcceleration": IBaseSetting<boolean>;
|
||||
"Electron.enableContentProtection": IBaseSetting<boolean>;
|
||||
"mediaPreviewConfig": IBaseSetting<MediaPreviewConfig>;
|
||||
"inviteRules": IBaseSetting<ComputedInviteConfig>;
|
||||
"Developer.elementCallUrl": IBaseSetting<string>;
|
||||
}
|
||||
|
||||
@@ -434,6 +437,11 @@ export const SETTINGS: Settings = {
|
||||
supportedLevels: LEVELS_ROOM_SETTINGS,
|
||||
default: MediaPreviewConfigController.default,
|
||||
},
|
||||
"inviteRules": {
|
||||
controller: new InviteRulesConfigController(),
|
||||
supportedLevels: [SettingLevel.ACCOUNT],
|
||||
default: InviteRulesConfigController.default,
|
||||
},
|
||||
"feature_report_to_moderators": {
|
||||
isFeature: true,
|
||||
labsGroup: LabGroup.Moderation,
|
||||
|
88
src/settings/controllers/InviteRulesConfigController.ts
Normal file
88
src/settings/controllers/InviteRulesConfigController.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type MatrixClient, type IContent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { type SettingLevel } from "../SettingLevel.ts";
|
||||
import MatrixClientBackedController from "./MatrixClientBackedController.ts";
|
||||
import {
|
||||
type ComputedInviteConfig as ComputedInviteRules,
|
||||
INVITE_RULES_ACCOUNT_DATA_TYPE,
|
||||
type InviteConfigAccountData,
|
||||
} from "../../@types/invite-rules.ts";
|
||||
import { _t } from "../../languageHandler.tsx";
|
||||
|
||||
/**
|
||||
* Handles invite filtering rules provided by MSC4155.
|
||||
* This handler does not make use of the roomId parameter.
|
||||
*/
|
||||
export default class InviteRulesConfigController extends MatrixClientBackedController {
|
||||
public static readonly default: ComputedInviteRules = {
|
||||
allBlocked: false,
|
||||
};
|
||||
|
||||
private static getValidSettingData(content: IContent): ComputedInviteRules {
|
||||
const expectedConfig = content as InviteConfigAccountData;
|
||||
return {
|
||||
allBlocked: !!expectedConfig.blocked_users?.includes("*"),
|
||||
};
|
||||
}
|
||||
|
||||
public initMatrixClient(newClient: MatrixClient): void {
|
||||
newClient.doesServerSupportUnstableFeature("org.matrix.msc4155").then((result) => {
|
||||
this.featureSupported = result;
|
||||
});
|
||||
}
|
||||
|
||||
public featureSupported?: boolean;
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
this.featureSupported = false;
|
||||
}
|
||||
|
||||
private getValue = (): ComputedInviteRules => {
|
||||
const accountData =
|
||||
this.client?.getAccountData(INVITE_RULES_ACCOUNT_DATA_TYPE)?.getContent<InviteConfigAccountData>() ?? {};
|
||||
return InviteRulesConfigController.getValidSettingData(accountData);
|
||||
};
|
||||
|
||||
public getValueOverride(_level: SettingLevel): ComputedInviteRules {
|
||||
return this.getValue();
|
||||
}
|
||||
|
||||
public get settingDisabled(): true | string {
|
||||
return this.featureSupported ? true : _t("settings|not_supported");
|
||||
}
|
||||
|
||||
public async beforeChange(
|
||||
_level: SettingLevel,
|
||||
_roomId: string | null,
|
||||
newValue: ComputedInviteRules,
|
||||
): Promise<boolean> {
|
||||
if (!this.client) {
|
||||
return false;
|
||||
}
|
||||
const existingContent = this.client
|
||||
.getAccountData(INVITE_RULES_ACCOUNT_DATA_TYPE)
|
||||
?.getContent<InviteConfigAccountData>();
|
||||
const newContent: InviteConfigAccountData = {
|
||||
...existingContent,
|
||||
blocked_users: [...(existingContent?.blocked_users ?? [])],
|
||||
};
|
||||
if (newValue.allBlocked && !newContent.blocked_users!.includes("*")) {
|
||||
newContent.blocked_users!.push("*");
|
||||
} else if (!newValue.allBlocked && newContent.blocked_users?.includes("*")) {
|
||||
newContent.blocked_users = newContent.blocked_users.filter((u) => u !== "*");
|
||||
} else {
|
||||
// No changes required.
|
||||
return false;
|
||||
}
|
||||
await this.client.setAccountData(INVITE_RULES_ACCOUNT_DATA_TYPE, newContent);
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -204,6 +204,9 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
|
||||
case "mediaPreviewConfig":
|
||||
// Handled in MediaPreviewConfigController.
|
||||
return;
|
||||
case "inviteRules":
|
||||
// Handled in InviteRulesConfigController.
|
||||
return;
|
||||
default:
|
||||
return this.setAccountData(DEFAULT_SETTINGS_EVENT_TYPE, settingName, newValue);
|
||||
}
|
||||
|
@@ -122,6 +122,9 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin
|
||||
case "mediaPreviewConfig":
|
||||
// Handled in MediaPreviewConfigController.
|
||||
return;
|
||||
case "inviteRules":
|
||||
// Handled in InviteRulesConfigController.
|
||||
return;
|
||||
default:
|
||||
return this.setRoomAccountData(roomId, DEFAULT_SETTINGS_EVENT_TYPE, settingName, newValue);
|
||||
}
|
||||
|
@@ -127,7 +127,7 @@ describe("<MatrixChat />", () => {
|
||||
setGuest: jest.fn(),
|
||||
setNotifTimelineSet: jest.fn(),
|
||||
getAccountData: jest.fn(),
|
||||
doesServerSupportUnstableFeature: jest.fn(),
|
||||
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(false),
|
||||
getDevices: jest.fn().mockResolvedValue({ devices: [] }),
|
||||
getProfileInfo: jest.fn().mockResolvedValue({
|
||||
displayname: "Ernie",
|
||||
|
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
import React from "react";
|
||||
import { render } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import { InviteRulesAccountSetting } from "../../../../../../../src/components/views/settings/tabs/user/InviteRulesAccountSettings";
|
||||
import SettingsStore from "../../../../../../../src/settings/SettingsStore";
|
||||
import { type ComputedInviteConfig } from "../../../../../../../src/@types/invite-rules";
|
||||
import { SettingLevel } from "../../../../../../../src/settings/SettingLevel";
|
||||
|
||||
function mockSetting(mediaPreviews: ComputedInviteConfig, supported = true) {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName) => {
|
||||
if (settingName === "inviteRules") {
|
||||
return mediaPreviews;
|
||||
}
|
||||
throw Error(`Unexpected setting ${settingName}`);
|
||||
});
|
||||
jest.spyOn(SettingsStore, "disabledMessage").mockImplementation((settingName) => {
|
||||
if (settingName === "inviteRules") {
|
||||
return supported ? undefined : "test-not-supported";
|
||||
}
|
||||
throw Error(`Unexpected setting ${settingName}`);
|
||||
});
|
||||
}
|
||||
|
||||
describe("InviteRulesAccountSetting", () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("does not render if not supported", async () => {
|
||||
mockSetting({ allBlocked: false }, false);
|
||||
const { findByText, findByLabelText } = render(<InviteRulesAccountSetting />);
|
||||
const input = await findByLabelText("Allow users to invite you to rooms");
|
||||
await userEvent.hover(input);
|
||||
const result = await findByText("test-not-supported");
|
||||
expect(result).toBeInTheDocument();
|
||||
});
|
||||
it("renders correct state when invites are not blocked", async () => {
|
||||
mockSetting({ allBlocked: false }, true);
|
||||
const { findByLabelText } = render(<InviteRulesAccountSetting />);
|
||||
const result = await findByLabelText("Allow users to invite you to rooms");
|
||||
expect(result).toBeChecked();
|
||||
});
|
||||
it("renders correct state when invites are blocked", async () => {
|
||||
mockSetting({ allBlocked: true }, true);
|
||||
const { findByLabelText } = render(<InviteRulesAccountSetting />);
|
||||
const result = await findByLabelText("Allow users to invite you to rooms");
|
||||
expect(result).not.toBeChecked();
|
||||
});
|
||||
it("handles disabling all invites", async () => {
|
||||
mockSetting({ allBlocked: false }, true);
|
||||
jest.spyOn(SettingsStore, "setValue").mockImplementation();
|
||||
const { findByLabelText } = render(<InviteRulesAccountSetting />);
|
||||
const result = await findByLabelText("Allow users to invite you to rooms");
|
||||
await userEvent.click(result);
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith("inviteRules", null, SettingLevel.ACCOUNT, {
|
||||
allBlocked: true,
|
||||
});
|
||||
});
|
||||
it("handles enabling all invites", async () => {
|
||||
mockSetting({ allBlocked: true }, true);
|
||||
jest.spyOn(SettingsStore, "setValue").mockImplementation();
|
||||
const { findByLabelText } = render(<InviteRulesAccountSetting />);
|
||||
const result = await findByLabelText("Allow users to invite you to rooms");
|
||||
await userEvent.click(result);
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith("inviteRules", null, SettingLevel.ACCOUNT, {
|
||||
allBlocked: false,
|
||||
});
|
||||
});
|
||||
});
|
@@ -1297,6 +1297,36 @@ exports[`PreferencesUserSettingsTab should render 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form
|
||||
class="_root_19upo_16 mx_MediaPreviewAccountSetting_Form"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsFlag mx_MediaPreviewAccountSetting_ToggleSwitch"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_label"
|
||||
>
|
||||
<div
|
||||
id="mx_LabelledToggleSwitch_«re»"
|
||||
>
|
||||
Allow users to invite you to rooms
|
||||
</div>
|
||||
</span>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="true"
|
||||
aria-label="Your server does not implement this feature."
|
||||
aria-labelledby="mx_LabelledToggleSwitch_«re»"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_7ckbw_8"
|
||||
|
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import MatrixClientBackedController from "../../../../src/settings/controllers/MatrixClientBackedController";
|
||||
import InviteRulesConfigController from "../../../../src/settings/controllers/InviteRulesConfigController";
|
||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
import { getMockClientWithEventEmitter, mockClientMethodsServer } from "../../../test-utils";
|
||||
import { INVITE_RULES_ACCOUNT_DATA_TYPE, type InviteConfigAccountData } from "../../../../src/@types/invite-rules";
|
||||
|
||||
describe("InviteRulesConfigController", () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("gets the default settings when none are specified.", () => {
|
||||
const controller = new InviteRulesConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(null),
|
||||
});
|
||||
|
||||
const value = controller.getValueOverride(SettingLevel.ACCOUNT);
|
||||
expect(value).toEqual(InviteRulesConfigController.default);
|
||||
});
|
||||
|
||||
it("gets the default settings when the setting is empty.", () => {
|
||||
const controller = new InviteRulesConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest
|
||||
.fn()
|
||||
.mockReturnValue(new MatrixEvent({ type: INVITE_RULES_ACCOUNT_DATA_TYPE, content: {} })),
|
||||
});
|
||||
|
||||
const value = controller.getValueOverride(SettingLevel.ACCOUNT);
|
||||
expect(value).toEqual(InviteRulesConfigController.default);
|
||||
});
|
||||
|
||||
it.each<InviteConfigAccountData>([{ blocked_users: ["foo_bar"] }, { blocked_users: [] }, {}])(
|
||||
"calculates blockAll to be false",
|
||||
(content: InviteConfigAccountData) => {
|
||||
const controller = new InviteRulesConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: INVITE_RULES_ACCOUNT_DATA_TYPE,
|
||||
content,
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
const globalValue = controller.getValueOverride(SettingLevel.ACCOUNT);
|
||||
expect(globalValue.allBlocked).toEqual(false);
|
||||
},
|
||||
);
|
||||
|
||||
it.each<InviteConfigAccountData>([
|
||||
{ blocked_users: ["*"] },
|
||||
{ blocked_users: ["*", "bob"] },
|
||||
{ allowed_users: ["*"], blocked_users: ["*"] },
|
||||
])("calculates blockAll to be true", (content: InviteConfigAccountData) => {
|
||||
const controller = new InviteRulesConfigController();
|
||||
|
||||
MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: INVITE_RULES_ACCOUNT_DATA_TYPE,
|
||||
content,
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
const globalValue = controller.getValueOverride(SettingLevel.ACCOUNT);
|
||||
expect(globalValue.allBlocked).toEqual(true);
|
||||
});
|
||||
|
||||
it("sets the account data correctly for blockAll = true", async () => {
|
||||
const controller = new InviteRulesConfigController();
|
||||
const client = (MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: INVITE_RULES_ACCOUNT_DATA_TYPE,
|
||||
content: {
|
||||
existing_content: {},
|
||||
allowed_servers: ["*"],
|
||||
},
|
||||
}),
|
||||
),
|
||||
setAccountData: jest.fn(),
|
||||
}));
|
||||
|
||||
expect(await controller.beforeChange(SettingLevel.ACCOUNT, null, { allBlocked: true })).toBe(true);
|
||||
expect(client.setAccountData).toHaveBeenCalledWith(INVITE_RULES_ACCOUNT_DATA_TYPE, {
|
||||
existing_content: {},
|
||||
allowed_servers: ["*"],
|
||||
blocked_users: ["*"],
|
||||
});
|
||||
});
|
||||
|
||||
it("sets the account data correctly for blockAll = false", async () => {
|
||||
const controller = new InviteRulesConfigController();
|
||||
const client = (MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: INVITE_RULES_ACCOUNT_DATA_TYPE,
|
||||
content: {
|
||||
existing_content: {},
|
||||
allowed_servers: ["*"],
|
||||
blocked_users: ["extra_user", "*"],
|
||||
},
|
||||
}),
|
||||
),
|
||||
setAccountData: jest.fn(),
|
||||
}));
|
||||
|
||||
expect(await controller.beforeChange(SettingLevel.ACCOUNT, null, { allBlocked: false })).toBe(true);
|
||||
expect(client.setAccountData).toHaveBeenCalledWith(INVITE_RULES_ACCOUNT_DATA_TYPE, {
|
||||
existing_content: {},
|
||||
allowed_servers: ["*"],
|
||||
blocked_users: ["extra_user"],
|
||||
});
|
||||
});
|
||||
it.each([true, false])("ignores a no-op when allBlocked = %s", async (allBlocked) => {
|
||||
const controller = new InviteRulesConfigController();
|
||||
const client = (MatrixClientBackedController.matrixClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
getAccountData: jest.fn().mockReturnValue(
|
||||
new MatrixEvent({
|
||||
type: INVITE_RULES_ACCOUNT_DATA_TYPE,
|
||||
content: {
|
||||
existing_content: {},
|
||||
allowed_servers: ["*"],
|
||||
blocked_users: allBlocked ? ["*"] : [],
|
||||
},
|
||||
}),
|
||||
),
|
||||
setAccountData: jest.fn(),
|
||||
}));
|
||||
|
||||
expect(await controller.beforeChange(SettingLevel.ACCOUNT, null, { allBlocked })).toBe(false);
|
||||
expect(client.setAccountData).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user