Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/components/structures/SpaceRoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@ const SpaceSetupFirstRooms: React.FC<{
return createRoom(space.client, {
createOpts: {
preset: isPublic ? Preset.PublicChat : Preset.PrivateChat,
name,
},
name,
spinner: false,
encryption: false,
andView: false,
Expand Down Expand Up @@ -423,7 +423,7 @@ const SpaceSetupPublicShare: React.FC<ISpaceSetupPublicShareProps> = ({
<div className="mx_SpaceRoomView_publicShare">
<h1>
{_t("create_space|share_heading", {
name: justCreatedOpts?.createOpts?.name || space.name,
name: justCreatedOpts?.name || space.name,
})}
</h1>
<div className="mx_SpaceRoomView_description">{_t("create_space|share_description")}</div>
Expand All @@ -449,7 +449,7 @@ const SpaceSetupPrivateScope: React.FC<{
<h1>{_t("create_space|private_personal_heading")}</h1>
<div className="mx_SpaceRoomView_description">
{_t("create_space|private_personal_description", {
name: justCreatedOpts?.createOpts?.name || space.name,
name: justCreatedOpts?.name || space.name,
})}
</div>

Expand Down Expand Up @@ -686,7 +686,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
<SpaceSetupFirstRooms
space={this.props.space}
title={_t("create_space|setup_rooms_community_heading", {
spaceName: this.props.justCreatedOpts?.createOpts?.name || this.props.space.name,
spaceName: this.props.justCreatedOpts?.name || this.props.space.name,
})}
description={
<>
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/dialogs/CreateRoomDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
const opts: IOpts = {};
const createOpts: IOpts["createOpts"] = (opts.createOpts = {});
opts.roomType = this.props.type;
createOpts.name = this.state.name;
opts.name = this.state.name;

if (this.state.joinRule === JoinRule.Public) {
createOpts.visibility = Visibility.Public;
Expand All @@ -139,7 +139,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
}

if (this.state.topic) {
createOpts.topic = this.state.topic;
opts.topic = this.state.topic;
}
if (this.state.noFederate) {
createOpts.creation_content = { "m.federate": false };
Expand Down
22 changes: 21 additions & 1 deletion src/createRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,19 @@ import { ElementCallEventType, ElementCallMemberEventType } from "./call-types";

export interface IOpts {
dmUserId?: string;
createOpts?: ICreateRoomOpts;
/**
* The name of the room to be created.
*/
name?: string;
/**
* The topic for the room.
*/
topic?: string;
/**
* Additional options to pass to the room creation API.
* Note: "name", "topic", and "avatar" should be set via their respective properties in IOpts.
*/
createOpts?: Omit<ICreateRoomOpts, "name" | "topic" | "avatar">;
spinner?: boolean;
guestAccess?: boolean;
encryption?: boolean;
Expand Down Expand Up @@ -251,6 +263,14 @@ export default async function createRoom(client: MatrixClient, opts: IOpts): Pro
});
}

if (opts.name) {
createOpts.name = opts.name;
}

if (opts.topic) {
createOpts.topic = opts.topic;
}

if (opts.avatar) {
let url = opts.avatar;
if (opts.avatar instanceof File) {
Expand Down
160 changes: 152 additions & 8 deletions test/unit-tests/components/views/dialogs/CreateRoomDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ Please see LICENSE files in the repository root for full details.

import React from "react";
import { fireEvent, render, screen, within } from "jest-matrix-react";
import { JoinRule, MatrixError, Preset, Visibility } from "matrix-js-sdk/src/matrix";
import { type Room, JoinRule, MatrixError, Preset, Visibility } from "matrix-js-sdk/src/matrix";

import CreateRoomDialog from "../../../../../src/components/views/dialogs/CreateRoomDialog";
import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../../test-utils";
import { flushPromises, getMockClientWithEventEmitter, mkSpace, mockClientMethodsUser } from "../../../../test-utils";
import SettingsStore from "../../../../../src/settings/SettingsStore";
import { UIFeature } from "../../../../../src/settings/UIFeature";

Expand Down Expand Up @@ -58,6 +58,55 @@ describe("<CreateRoomDialog />", () => {
expect(screen.getByLabelText("Name")).toHaveDisplayValue(defaultName);
});

it("should include topic in room creation options", async () => {
const onFinished = jest.fn();
render(<CreateRoomDialog onFinished={onFinished} />);
await flushPromises();

const topic = "This is a test topic";

// Set room name and topic.
fireEvent.change(screen.getByLabelText("Name"), { target: { value: "Room with topic" } });
fireEvent.change(screen.getByLabelText("Topic (optional)"), { target: { value: topic } });

// Create the room.
fireEvent.click(screen.getByText("Create room"));
await flushPromises();

expect(onFinished).toHaveBeenCalledWith(
true,
expect.objectContaining({
name: "Room with topic",
topic,
}),
);
});

it("should include no federate option in room creation options when enabled", async () => {
const onFinished = jest.fn();
render(<CreateRoomDialog onFinished={onFinished} />);
await flushPromises();

// Set room name, and disable federation.
fireEvent.change(screen.getByLabelText("Name"), { target: { value: "NoFederate Room" } });
fireEvent.click(screen.getByLabelText("Block anyone not part of server.org from ever joining this room."));

fireEvent.click(screen.getByText("Create room"));
await flushPromises();

expect(onFinished).toHaveBeenCalledWith(
true,
expect.objectContaining({
name: "NoFederate Room",
createOpts: expect.objectContaining({
creation_content: expect.objectContaining({
"m.federate": false,
}),
}),
}),
);
});

describe("for a private room", () => {
// default behaviour is a private room

Expand Down Expand Up @@ -198,9 +247,8 @@ describe("<CreateRoomDialog />", () => {
await flushPromises();

expect(onFinished).toHaveBeenCalledWith(true, {
createOpts: {
name: roomName,
},
createOpts: {},
name: roomName,
encryption: true,
parentSpace: undefined,
roomType: undefined,
Expand Down Expand Up @@ -259,9 +307,9 @@ describe("<CreateRoomDialog />", () => {
await flushPromises();
expect(onFinished).toHaveBeenCalledWith(true, {
createOpts: {
name: roomName,
visibility: Visibility.Private,
},
name: roomName,
encryption: true,
joinRule: JoinRule.Knock,
parentSpace: undefined,
Expand All @@ -277,9 +325,9 @@ describe("<CreateRoomDialog />", () => {
await flushPromises();
expect(onFinished).toHaveBeenCalledWith(true, {
createOpts: {
name: roomName,
visibility: Visibility.Public,
},
name: roomName,
encryption: true,
joinRule: JoinRule.Knock,
parentSpace: undefined,
Expand Down Expand Up @@ -349,15 +397,111 @@ describe("<CreateRoomDialog />", () => {

expect(onFinished).toHaveBeenCalledWith(true, {
createOpts: {
name: roomName,
preset: Preset.PublicChat,
room_alias_name: roomAlias,
visibility: Visibility.Public,
},
name: roomName,
guestAccess: false,
parentSpace: undefined,
roomType: undefined,
});
});
});

describe("for a room in a space", () => {
let parentSpace: Room;
beforeEach(() => {
parentSpace = mkSpace(mockClient, "!space:server") as unknown as Room;
});

it("should create a room with restricted join rule when selected", async () => {
const onFinished = jest.fn();
render(<CreateRoomDialog parentSpace={parentSpace} onFinished={onFinished} />);
await flushPromises();

// Set room name and visibility.
fireEvent.change(screen.getByLabelText("Name"), { target: { value: "Restricted Room" } });
fireEvent.click(screen.getByLabelText("Room visibility"));
fireEvent.click(screen.getByRole("option", { name: "Visible to space members" }));

fireEvent.click(screen.getByText("Create room"));
await flushPromises();

expect(onFinished).toHaveBeenCalledWith(
true,
expect.objectContaining({
name: "Restricted Room",
joinRule: JoinRule.Restricted,
}),
);
});

it("should create a room with public join rule when selected", async () => {
const onFinished = jest.fn();
render(<CreateRoomDialog parentSpace={parentSpace} onFinished={onFinished} />);
await flushPromises();

// Set room name and visibility. Rooms in spaces also need an address.
fireEvent.change(screen.getByLabelText("Name"), { target: { value: "Public Room" } });
fireEvent.click(screen.getByLabelText("Room visibility"));
fireEvent.click(screen.getByRole("option", { name: "Public room" }));
fireEvent.change(screen.getByLabelText("Room address"), { target: { value: "testroom" } });

// Create the room.
fireEvent.click(screen.getByText("Create room"));
await flushPromises();

expect(onFinished).toHaveBeenCalledWith(
true,
expect.objectContaining({
name: "Public Room",
createOpts: expect.objectContaining({
room_alias_name: "testroom",
visibility: Visibility.Public,
preset: Preset.PublicChat,
}),
guestAccess: false,
roomType: undefined,
}),
);
});
});

describe("keyboard shortcuts", () => {
it("should submit the form when Enter is pressed", async () => {
const onFinished = jest.fn();
render(<CreateRoomDialog onFinished={onFinished} />);
await flushPromises();

// Simulate pressing the Enter key.
fireEvent.change(screen.getByLabelText("Name"), { target: { value: "Keyboard Room" } });
fireEvent.keyDown(screen.getByLabelText("Name"), { key: "Enter", code: "Enter", charCode: 13 });

await flushPromises();

expect(onFinished).toHaveBeenCalledWith(
true,
expect.objectContaining({
name: "Keyboard Room",
}),
);
});

it("should cancel the dialog when Escape is pressed", async () => {
const onFinished = jest.fn();
render(<CreateRoomDialog onFinished={onFinished} />);
await flushPromises();

// Simulate pressing the Escape key.
fireEvent.keyDown(screen.getByLabelText("Name"), { key: "Escape", code: "Escape", charCode: 27 });

await flushPromises();

// BaseDialog passes no arguments, but DialogButtons pass false - might not be desirable?
expect(onFinished).toHaveBeenCalled();
const callArgs = onFinished.mock.calls[0];
expect(callArgs.length === 0 || callArgs[0] === false).toBe(true);
});
});
});
Loading
Loading