Add UIFeature to hide public space and room creation (#30708)
* Add settings to hide public room & space creation. * Add space changes. * Add room changes. * lint * Add playwright tests * don't specialcase 1 join rule * Ensure mocks get cleared * Fixup test * Add SpaceCreateMenu component unit-tests * Fixup create room test asserts * fix import
This commit is contained in:
@@ -585,6 +585,8 @@ Currently, the following UI feature flags are supported:
|
|||||||
- `UIFeature.BulkUnverifiedSessionsReminder` - Display popup reminders to verify or remove unverified sessions. Defaults
|
- `UIFeature.BulkUnverifiedSessionsReminder` - Display popup reminders to verify or remove unverified sessions. Defaults
|
||||||
to true.
|
to true.
|
||||||
- `UIFeature.locationSharing` - Whether or not location sharing menus will be shown.
|
- `UIFeature.locationSharing` - Whether or not location sharing menus will be shown.
|
||||||
|
- `UIFeature.allowCreatingPublicRooms` - Whether or not public rooms can be created.
|
||||||
|
- `UIFeature.allowCreatingPublicSpaces` - Whether or not public spaces can be created.
|
||||||
|
|
||||||
## Undocumented / developer options
|
## Undocumented / developer options
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
||||||
|
import { UIFeature } from "../../../src/settings/UIFeature";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
|
|
||||||
const name = "Test room";
|
const name = "Test room";
|
||||||
@@ -65,4 +66,33 @@ test.describe("Create Room", () => {
|
|||||||
const header = page.locator(".mx_RoomHeader");
|
const header = page.locator(".mx_RoomHeader");
|
||||||
await expect(header).toContainText(name);
|
await expect(header).toContainText(name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe("Should hide public room option if not allowed", () => {
|
||||||
|
test.use({
|
||||||
|
config: {
|
||||||
|
setting_defaults: {
|
||||||
|
[UIFeature.AllowCreatingPublicRooms]: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should disallow creating public rooms", { tag: "@screenshot" }, async ({ page, user, app, axe }) => {
|
||||||
|
const dialog = await app.openCreateRoomDialog();
|
||||||
|
// Fill name & topic
|
||||||
|
await dialog.getByRole("textbox", { name: "Name" }).fill(name);
|
||||||
|
await dialog.getByRole("textbox", { name: "Topic" }).fill(topic);
|
||||||
|
|
||||||
|
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||||
|
await expect(axe).toHaveNoViolations();
|
||||||
|
// Snapshot it
|
||||||
|
await expect(dialog).toMatchScreenshot("create-room-no-public.png");
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
await dialog.getByRole("button", { name: "Create room" }).click();
|
||||||
|
|
||||||
|
await expect(page).toHaveURL(new RegExp(`/#/room/!.+`));
|
||||||
|
const header = page.locator(".mx_RoomHeader");
|
||||||
|
await expect(header).toContainText(name);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { test, expect } from "../../element-web-test";
|
|||||||
import type { Preset, ICreateRoomOpts } from "matrix-js-sdk/src/matrix";
|
import type { Preset, ICreateRoomOpts } from "matrix-js-sdk/src/matrix";
|
||||||
import { type ElementAppPage } from "../../pages/ElementAppPage";
|
import { type ElementAppPage } from "../../pages/ElementAppPage";
|
||||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
import { UIFeature } from "../../../src/settings/UIFeature";
|
||||||
|
|
||||||
async function openSpaceCreateMenu(page: Page): Promise<Locator> {
|
async function openSpaceCreateMenu(page: Page): Promise<Locator> {
|
||||||
await page.getByRole("button", { name: "Create a space" }).click();
|
await page.getByRole("button", { name: "Create a space" }).click();
|
||||||
@@ -391,4 +392,53 @@ test.describe("Spaces", () => {
|
|||||||
"space-visibility-settings.png",
|
"space-visibility-settings.png",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe("Should hide public spaces option if not allowed", () => {
|
||||||
|
test.use({
|
||||||
|
config: {
|
||||||
|
setting_defaults: {
|
||||||
|
[UIFeature.AllowCreatingPublicSpaces]: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should disallow creating public rooms", { tag: "@screenshot" }, async ({ page, user, app }) => {
|
||||||
|
const menu = await openSpaceCreateMenu(page);
|
||||||
|
await menu
|
||||||
|
.locator('.mx_SpaceBasicSettings_avatarContainer input[type="file"]')
|
||||||
|
.setInputFiles("playwright/sample-files/riot.png");
|
||||||
|
await menu.getByRole("textbox", { name: "Name" }).fill("This is a private space");
|
||||||
|
await expect(menu.getByRole("textbox", { name: "Address" })).not.toBeVisible();
|
||||||
|
await menu
|
||||||
|
.getByRole("textbox", { name: "Description" })
|
||||||
|
.fill("This is a private space because we can't make public ones");
|
||||||
|
await menu.getByRole("button", { name: "Create" }).click();
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Me and my teammates" }).click();
|
||||||
|
|
||||||
|
// Create the default General & Random rooms, as well as a custom "Projects" room
|
||||||
|
await expect(page.getByPlaceholder("General")).toBeVisible();
|
||||||
|
await expect(page.getByPlaceholder("Random")).toBeVisible();
|
||||||
|
await page.getByPlaceholder("Support").fill("Projects");
|
||||||
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
|
await page.getByRole("button", { name: "Skip for now" }).click();
|
||||||
|
|
||||||
|
// Assert rooms exist in the room list
|
||||||
|
const roomList = page.getByRole("tree", { name: "Rooms" });
|
||||||
|
await expect(roomList.getByRole("treeitem", { name: "General", exact: true })).toBeVisible();
|
||||||
|
await expect(roomList.getByRole("treeitem", { name: "Random", exact: true })).toBeVisible();
|
||||||
|
await expect(roomList.getByRole("treeitem", { name: "Projects", exact: true })).toBeVisible();
|
||||||
|
|
||||||
|
// Assert rooms exist in the space explorer
|
||||||
|
await expect(
|
||||||
|
page.locator(".mx_SpaceHierarchy_list .mx_SpaceHierarchy_roomTile", { hasText: "General" }),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.locator(".mx_SpaceHierarchy_list .mx_SpaceHierarchy_roomTile", { hasText: "Random" }),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.locator(".mx_SpaceHierarchy_list .mx_SpaceHierarchy_roomTile", { hasText: "Projects" }),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
@@ -85,6 +85,7 @@ interface IState {
|
|||||||
export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
||||||
private readonly askToJoinEnabled: boolean;
|
private readonly askToJoinEnabled: boolean;
|
||||||
private readonly advancedSettingsEnabled: boolean;
|
private readonly advancedSettingsEnabled: boolean;
|
||||||
|
private readonly allowCreatingPublicRooms: boolean;
|
||||||
private readonly supportsRestricted: boolean;
|
private readonly supportsRestricted: boolean;
|
||||||
private nameField = createRef<Field>();
|
private nameField = createRef<Field>();
|
||||||
private aliasField = createRef<RoomAliasField>();
|
private aliasField = createRef<RoomAliasField>();
|
||||||
@@ -94,11 +95,13 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
this.askToJoinEnabled = SettingsStore.getValue("feature_ask_to_join");
|
this.askToJoinEnabled = SettingsStore.getValue("feature_ask_to_join");
|
||||||
this.advancedSettingsEnabled = SettingsStore.getValue(UIFeature.AdvancedSettings);
|
this.advancedSettingsEnabled = SettingsStore.getValue(UIFeature.AdvancedSettings);
|
||||||
|
this.allowCreatingPublicRooms = SettingsStore.getValue(UIFeature.AllowCreatingPublicRooms);
|
||||||
|
|
||||||
this.supportsRestricted = !!this.props.parentSpace;
|
this.supportsRestricted = !!this.props.parentSpace;
|
||||||
|
const defaultPublic = this.allowCreatingPublicRooms && this.props.defaultPublic;
|
||||||
|
|
||||||
let joinRule = JoinRule.Invite;
|
let joinRule = JoinRule.Invite;
|
||||||
if (this.props.defaultPublic) {
|
if (defaultPublic) {
|
||||||
joinRule = JoinRule.Public;
|
joinRule = JoinRule.Public;
|
||||||
} else if (this.supportsRestricted) {
|
} else if (this.supportsRestricted) {
|
||||||
joinRule = JoinRule.Restricted;
|
joinRule = JoinRule.Restricted;
|
||||||
@@ -106,7 +109,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
const cli = MatrixClientPeg.safeGet();
|
const cli = MatrixClientPeg.safeGet();
|
||||||
this.state = {
|
this.state = {
|
||||||
isPublicKnockRoom: this.props.defaultPublic || false,
|
isPublicKnockRoom: defaultPublic || false,
|
||||||
isEncrypted: this.props.defaultEncrypted ?? privateShouldBeEncrypted(cli),
|
isEncrypted: this.props.defaultEncrypted ?? privateShouldBeEncrypted(cli),
|
||||||
joinRule,
|
joinRule,
|
||||||
name: this.props.defaultName || "",
|
name: this.props.defaultName || "",
|
||||||
@@ -419,7 +422,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
|
|||||||
labelKnock={
|
labelKnock={
|
||||||
this.askToJoinEnabled ? _t("room_settings|security|join_rule_knock") : undefined
|
this.askToJoinEnabled ? _t("room_settings|security|join_rule_knock") : undefined
|
||||||
}
|
}
|
||||||
labelPublic={_t("common|public_room")}
|
labelPublic={this.allowCreatingPublicRooms ? _t("common|public_room") : undefined}
|
||||||
labelRestricted={
|
labelRestricted={
|
||||||
this.supportsRestricted ? _t("create_room|join_rule_restricted") : undefined
|
this.supportsRestricted ? _t("create_room|join_rule_restricted") : undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ interface IProps {
|
|||||||
width?: number;
|
width?: number;
|
||||||
labelInvite: string;
|
labelInvite: string;
|
||||||
labelKnock?: string;
|
labelKnock?: string;
|
||||||
labelPublic: string;
|
labelPublic?: string;
|
||||||
labelRestricted?: string; // if omitted then this option will be hidden, e.g if unsupported
|
labelRestricted?: string; // if omitted then this option will be hidden, e.g if unsupported
|
||||||
onChange(value: JoinRule): void;
|
onChange(value: JoinRule): void;
|
||||||
}
|
}
|
||||||
@@ -38,11 +38,18 @@ const JoinRuleDropdown: React.FC<IProps> = ({
|
|||||||
<div key={JoinRule.Invite} className="mx_JoinRuleDropdown_invite">
|
<div key={JoinRule.Invite} className="mx_JoinRuleDropdown_invite">
|
||||||
{labelInvite}
|
{labelInvite}
|
||||||
</div>,
|
</div>,
|
||||||
<div key={JoinRule.Public} className="mx_JoinRuleDropdown_public">
|
|
||||||
{labelPublic}
|
|
||||||
</div>,
|
|
||||||
] as NonEmptyArray<ReactElement & { key: string }>;
|
] as NonEmptyArray<ReactElement & { key: string }>;
|
||||||
|
|
||||||
|
if (labelPublic) {
|
||||||
|
options.push(
|
||||||
|
(
|
||||||
|
<div key={JoinRule.Public} className="mx_JoinRuleDropdown_public">
|
||||||
|
{labelPublic}
|
||||||
|
</div>
|
||||||
|
) as ReactElement & { key: string },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (labelKnock) {
|
if (labelKnock) {
|
||||||
options.unshift(
|
options.unshift(
|
||||||
(
|
(
|
||||||
@@ -72,6 +79,7 @@ const JoinRuleDropdown: React.FC<IProps> = ({
|
|||||||
menuWidth={width}
|
menuWidth={width}
|
||||||
value={value}
|
value={value}
|
||||||
label={label}
|
label={label}
|
||||||
|
disabled={options.length === 1}
|
||||||
>
|
>
|
||||||
{options}
|
{options}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ import defaultDispatcher from "../../../dispatcher/dispatcher";
|
|||||||
import { Action } from "../../../dispatcher/actions";
|
import { Action } from "../../../dispatcher/actions";
|
||||||
import { Filter } from "../dialogs/spotlight/Filter";
|
import { Filter } from "../dialogs/spotlight/Filter";
|
||||||
import { type OpenSpotlightPayload } from "../../../dispatcher/payloads/OpenSpotlightPayload.ts";
|
import { type OpenSpotlightPayload } from "../../../dispatcher/payloads/OpenSpotlightPayload.ts";
|
||||||
|
import { useSettingValue } from "../../../hooks/useSettings.ts";
|
||||||
|
import { UIFeature } from "../../../settings/UIFeature.ts";
|
||||||
|
|
||||||
export const createSpace = async (
|
export const createSpace = async (
|
||||||
client: MatrixClient,
|
client: MatrixClient,
|
||||||
@@ -212,7 +214,10 @@ const SpaceCreateMenu: React.FC<{
|
|||||||
onFinished(): void;
|
onFinished(): void;
|
||||||
}> = ({ onFinished }) => {
|
}> = ({ onFinished }) => {
|
||||||
const cli = useMatrixClientContext();
|
const cli = useMatrixClientContext();
|
||||||
const [visibility, setVisibility] = useState<Visibility | null>(null);
|
const settingAllowPublicSpaces = useSettingValue(UIFeature.AllowCreatingPublicSpaces);
|
||||||
|
const [visibility, setVisibility] = useState<Visibility | null>(
|
||||||
|
settingAllowPublicSpaces === false ? Visibility.Private : null,
|
||||||
|
);
|
||||||
const [busy, setBusy] = useState<boolean>(false);
|
const [busy, setBusy] = useState<boolean>(false);
|
||||||
|
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
@@ -303,16 +308,20 @@ const SpaceCreateMenu: React.FC<{
|
|||||||
} else {
|
} else {
|
||||||
body = (
|
body = (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<AccessibleButton
|
{settingAllowPublicSpaces && (
|
||||||
className="mx_SpaceCreateMenu_back"
|
<AccessibleButton
|
||||||
onClick={() => setVisibility(null)}
|
className="mx_SpaceCreateMenu_back"
|
||||||
title={_t("action|go_back")}
|
onClick={() => setVisibility(null)}
|
||||||
/>
|
title={_t("action|go_back")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
{visibility === Visibility.Public
|
{visibility === Visibility.Public
|
||||||
? _t("create_space|public_heading")
|
? _t("create_space|public_heading")
|
||||||
: _t("create_space|private_heading")}
|
: settingAllowPublicSpaces
|
||||||
|
? _t("create_space|private_heading")
|
||||||
|
: _t("create_space|private_only_heading")}
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
{_t("create_space|add_details_prompt")} {_t("create_space|add_details_prompt_2")}
|
{_t("create_space|add_details_prompt")} {_t("create_space|add_details_prompt_2")}
|
||||||
|
|||||||
@@ -718,6 +718,7 @@
|
|||||||
"personal_space_description": "A private space to organise your rooms",
|
"personal_space_description": "A private space to organise your rooms",
|
||||||
"private_description": "Invite only, best for yourself or teams",
|
"private_description": "Invite only, best for yourself or teams",
|
||||||
"private_heading": "Your private space",
|
"private_heading": "Your private space",
|
||||||
|
"private_only_heading": "Your space",
|
||||||
"private_personal_description": "Make sure the right people have access to %(name)s",
|
"private_personal_description": "Make sure the right people have access to %(name)s",
|
||||||
"private_personal_heading": "Who are you working with?",
|
"private_personal_heading": "Who are you working with?",
|
||||||
"private_space": "Me and my teammates",
|
"private_space": "Me and my teammates",
|
||||||
|
|||||||
@@ -1425,6 +1425,14 @@ export const SETTINGS: Settings = {
|
|||||||
supportedLevels: LEVELS_UI_FEATURE,
|
supportedLevels: LEVELS_UI_FEATURE,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
[UIFeature.AllowCreatingPublicSpaces]: {
|
||||||
|
supportedLevels: LEVELS_UI_FEATURE,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
[UIFeature.AllowCreatingPublicRooms]: {
|
||||||
|
supportedLevels: LEVELS_UI_FEATURE,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
|
||||||
// Electron-specific settings, they are stored by Electron and set/read over an IPC.
|
// Electron-specific settings, they are stored by Electron and set/read over an IPC.
|
||||||
// We store them over there are they are necessary to know before the renderer process launches.
|
// We store them over there are they are necessary to know before the renderer process launches.
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export const enum UIFeature {
|
|||||||
RoomHistorySettings = "UIFeature.roomHistorySettings",
|
RoomHistorySettings = "UIFeature.roomHistorySettings",
|
||||||
TimelineEnableRelativeDates = "UIFeature.timelineEnableRelativeDates",
|
TimelineEnableRelativeDates = "UIFeature.timelineEnableRelativeDates",
|
||||||
BulkUnverifiedSessionsReminder = "UIFeature.BulkUnverifiedSessionsReminder",
|
BulkUnverifiedSessionsReminder = "UIFeature.BulkUnverifiedSessionsReminder",
|
||||||
|
AllowCreatingPublicRooms = "UIFeature.allowCreatingPublicRooms",
|
||||||
|
AllowCreatingPublicSpaces = "UIFeature.allowCreatingPublicSpaces",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum UIComponent {
|
export enum UIComponent {
|
||||||
|
|||||||
@@ -17,25 +17,30 @@ import { UIFeature } from "../../../../../src/settings/UIFeature";
|
|||||||
|
|
||||||
describe("<CreateRoomDialog />", () => {
|
describe("<CreateRoomDialog />", () => {
|
||||||
const userId = "@alice:server.org";
|
const userId = "@alice:server.org";
|
||||||
const mockClient = getMockClientWithEventEmitter({
|
|
||||||
...mockClientMethodsUser(userId),
|
|
||||||
getDomain: jest.fn().mockReturnValue("server.org"),
|
|
||||||
getClientWellKnown: jest.fn(),
|
|
||||||
doesServerForceEncryptionForPreset: jest.fn(),
|
|
||||||
// make every alias available
|
|
||||||
getRoomIdForAlias: jest.fn().mockRejectedValue(new MatrixError({ errcode: "M_NOT_FOUND" })),
|
|
||||||
});
|
|
||||||
|
|
||||||
const getE2eeEnableToggleInputElement = () => screen.getByLabelText("Enable end-to-end encryption");
|
const getE2eeEnableToggleInputElement = () => screen.getByLabelText("Enable end-to-end encryption");
|
||||||
// labelled toggle switch doesn't set the disabled attribute, only aria-disabled
|
// labelled toggle switch doesn't set the disabled attribute, only aria-disabled
|
||||||
const getE2eeEnableToggleIsDisabled = () =>
|
const getE2eeEnableToggleIsDisabled = () =>
|
||||||
getE2eeEnableToggleInputElement().getAttribute("aria-disabled") === "true";
|
getE2eeEnableToggleInputElement().getAttribute("aria-disabled") === "true";
|
||||||
|
|
||||||
|
let mockClient: ReturnType<typeof getMockClientWithEventEmitter>;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
mockClient = getMockClientWithEventEmitter({
|
||||||
|
...mockClientMethodsUser(userId),
|
||||||
|
getDomain: jest.fn().mockReturnValue("server.org"),
|
||||||
|
getClientWellKnown: jest.fn(),
|
||||||
|
doesServerForceEncryptionForPreset: jest.fn(),
|
||||||
|
// make every alias available
|
||||||
|
getRoomIdForAlias: jest.fn().mockRejectedValue(new MatrixError({ errcode: "M_NOT_FOUND" })),
|
||||||
|
});
|
||||||
mockClient.doesServerForceEncryptionForPreset.mockResolvedValue(false);
|
mockClient.doesServerForceEncryptionForPreset.mockResolvedValue(false);
|
||||||
mockClient.getClientWellKnown.mockReturnValue({});
|
mockClient.getClientWellKnown.mockReturnValue({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
const getComponent = (props = {}) => render(<CreateRoomDialog onFinished={jest.fn()} {...props} />);
|
const getComponent = (props = {}) => render(<CreateRoomDialog onFinished={jest.fn()} {...props} />);
|
||||||
|
|
||||||
it("should default to private room", async () => {
|
it("should default to private room", async () => {
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
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 OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { render, cleanup } from "jest-matrix-react";
|
||||||
|
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { MatrixError } from "matrix-js-sdk/src/matrix";
|
||||||
|
import { type MockedObject } from "jest-mock";
|
||||||
|
|
||||||
|
import SpaceCreateMenu from "../../../../../src/components/views/spaces/SpaceCreateMenu";
|
||||||
|
import {
|
||||||
|
getMockClientWithEventEmitter,
|
||||||
|
mockClientMethodsRooms,
|
||||||
|
mockClientMethodsServer,
|
||||||
|
mockClientMethodsUser,
|
||||||
|
withClientContextRenderOptions,
|
||||||
|
} from "../../../../test-utils";
|
||||||
|
import { UIFeature } from "../../../../../src/settings/UIFeature";
|
||||||
|
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||||
|
|
||||||
|
describe("<SpaceCreateMenu />", () => {
|
||||||
|
let client: MockedObject<MatrixClient>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
client = getMockClientWithEventEmitter({
|
||||||
|
...mockClientMethodsUser(),
|
||||||
|
...mockClientMethodsServer(),
|
||||||
|
...mockClientMethodsRooms(),
|
||||||
|
createRoom: jest.fn(),
|
||||||
|
getRoomIdForAlias: jest.fn().mockImplementation(async () => {
|
||||||
|
throw new MatrixError({ errcode: "M_NOT_FOUND", error: "Test says no alias found" }, 404);
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render", async () => {
|
||||||
|
const { asFragment } = render(
|
||||||
|
<SpaceCreateMenu onFinished={jest.fn()} />,
|
||||||
|
withClientContextRenderOptions(client),
|
||||||
|
);
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to create a public space", async () => {
|
||||||
|
const onFinished = jest.fn();
|
||||||
|
client.createRoom.mockResolvedValue({ room_id: "!room:id" });
|
||||||
|
const { getByText, getByLabelText } = render(
|
||||||
|
<SpaceCreateMenu onFinished={onFinished} />,
|
||||||
|
withClientContextRenderOptions(client),
|
||||||
|
);
|
||||||
|
await userEvent.click(getByText("Public"));
|
||||||
|
await userEvent.type(getByLabelText("Name"), "My Name");
|
||||||
|
await userEvent.type(getByLabelText("Address"), "foobar");
|
||||||
|
await userEvent.type(getByLabelText("Description"), "A description");
|
||||||
|
await userEvent.click(getByText("Create"));
|
||||||
|
expect(onFinished).toHaveBeenCalledTimes(1);
|
||||||
|
expect(client.createRoom).toHaveBeenCalledWith({
|
||||||
|
creation_content: { type: "m.space" },
|
||||||
|
initial_state: [
|
||||||
|
{ content: { guest_access: "can_join" }, state_key: "", type: "m.room.guest_access" },
|
||||||
|
{ content: { history_visibility: "world_readable" }, type: "m.room.history_visibility" },
|
||||||
|
],
|
||||||
|
name: "My Name",
|
||||||
|
power_level_content_override: {
|
||||||
|
events_default: 100,
|
||||||
|
invite: 0,
|
||||||
|
},
|
||||||
|
preset: "public_chat",
|
||||||
|
room_alias_name: "my-namefoobar",
|
||||||
|
topic: "A description",
|
||||||
|
visibility: "private",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be prompted to automatically create a private space when configured", async () => {
|
||||||
|
const realGetValue = SettingsStore.getValue;
|
||||||
|
jest.spyOn(SettingsStore, "getValue").mockImplementation((name, roomId) => {
|
||||||
|
if (name === UIFeature.AllowCreatingPublicSpaces) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return realGetValue(name, roomId);
|
||||||
|
});
|
||||||
|
const onFinished = jest.fn();
|
||||||
|
client.createRoom.mockResolvedValue({ room_id: "!room:id" });
|
||||||
|
const { getByText, getByLabelText } = render(
|
||||||
|
<SpaceCreateMenu onFinished={onFinished} />,
|
||||||
|
withClientContextRenderOptions(client),
|
||||||
|
);
|
||||||
|
await userEvent.type(getByLabelText("Name"), "My Name");
|
||||||
|
await userEvent.type(getByLabelText("Description"), "A description");
|
||||||
|
await userEvent.click(getByText("Create"));
|
||||||
|
expect(onFinished).toHaveBeenCalledTimes(1);
|
||||||
|
expect(client.createRoom).toHaveBeenCalledWith({
|
||||||
|
creation_content: { type: "m.space" },
|
||||||
|
initial_state: [
|
||||||
|
{ content: { guest_access: "can_join" }, state_key: "", type: "m.room.guest_access" },
|
||||||
|
{ content: { history_visibility: "invited" }, type: "m.room.history_visibility" },
|
||||||
|
],
|
||||||
|
name: "My Name",
|
||||||
|
power_level_content_override: {
|
||||||
|
events_default: 100,
|
||||||
|
invite: 50,
|
||||||
|
},
|
||||||
|
room_alias_name: undefined,
|
||||||
|
preset: "private_chat",
|
||||||
|
topic: "A description",
|
||||||
|
visibility: "private",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<SpaceCreateMenu /> should render 1`] = `<DocumentFragment />`;
|
||||||
Reference in New Issue
Block a user