/*
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 { mocked } from "jest-mock";
import { randomString } from "matrix-js-sdk/src/randomstring";
import { act, fireEvent, render, RenderResult } from "@testing-library/react";
import { EventType, MatrixClient, Room, GuestAccess, HistoryVisibility, JoinRule } from "matrix-js-sdk/src/matrix";
import _SpaceSettingsVisibilityTab from "../../../../src/components/views/spaces/SpaceSettingsVisibilityTab";
import {
createTestClient,
mkEvent,
wrapInMatrixClientContext,
mkSpace,
mockStateEventImplementation,
} from "../../../test-utils";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
const SpaceSettingsVisibilityTab = wrapInMatrixClientContext(_SpaceSettingsVisibilityTab);
jest.useFakeTimers();
describe("", () => {
const mockMatrixClient = createTestClient() as MatrixClient;
const makeJoinEvent = (rule: JoinRule = JoinRule.Invite) =>
mkEvent({
type: EventType.RoomJoinRules,
event: true,
content: {
join_rule: rule,
},
} as any);
const makeGuestAccessEvent = (rule: GuestAccess = GuestAccess.CanJoin) =>
mkEvent({
type: EventType.RoomGuestAccess,
event: true,
content: {
guest_access: rule,
},
} as any);
const makeHistoryEvent = (rule: HistoryVisibility = HistoryVisibility.Shared) =>
mkEvent({
type: EventType.RoomHistoryVisibility,
event: true,
content: {
history_visibility: rule,
},
} as any);
const mockSpaceId = "mock-space";
// TODO case for canonical
const makeMockSpace = (
client: MatrixClient,
joinRule: JoinRule = JoinRule.Invite,
guestRule: GuestAccess = GuestAccess.CanJoin,
historyRule: HistoryVisibility = HistoryVisibility.WorldReadable,
): Room => {
const events = [makeJoinEvent(joinRule), makeGuestAccessEvent(guestRule), makeHistoryEvent(historyRule)];
const space = mkSpace(client, mockSpaceId);
const getStateEvents = mockStateEventImplementation(events);
mocked(space.currentState).getStateEvents.mockImplementation(getStateEvents);
mocked(space.currentState).mayClientSendStateEvent.mockReturnValue(false);
space.getJoinRule.mockReturnValue(joinRule);
mocked(space.currentState).getJoinRule.mockReturnValue(joinRule);
return space as unknown as Room;
};
const defaultProps = {
matrixClient: mockMatrixClient,
space: makeMockSpace(mockMatrixClient),
closeSettingsFn: jest.fn(),
};
const getComponent = (props = {}) => {
return render();
};
const toggleGuestAccessSection = async ({ getByTestId }: RenderResult) => {
const toggleButton = getByTestId("toggle-guest-access-btn")!;
fireEvent.click(toggleButton);
};
const getGuestAccessToggle = ({ getByLabelText }: RenderResult) => getByLabelText("Enable guest access");
const getHistoryVisibilityToggle = ({ getByLabelText }: RenderResult) => getByLabelText("Preview Space");
const getErrorMessage = ({ getByTestId }: RenderResult) => getByTestId("space-settings-error")?.textContent;
beforeEach(() => {
let i = 0;
mocked(randomString).mockImplementation(() => {
return "testid_" + i++;
});
(mockMatrixClient.sendStateEvent as jest.Mock).mockClear().mockResolvedValue({});
MatrixClientPeg.get = jest.fn().mockReturnValue(mockMatrixClient);
MatrixClientPeg.safeGet = jest.fn().mockReturnValue(mockMatrixClient);
});
afterEach(() => {
jest.runAllTimers();
});
it("renders container", () => {
const { asFragment } = getComponent();
expect(asFragment()).toMatchSnapshot();
});
describe("for a private space", () => {
const joinRule = JoinRule.Invite;
it("does not render addresses section", () => {
const space = makeMockSpace(mockMatrixClient, joinRule);
const { queryByTestId } = getComponent({ space });
expect(queryByTestId("published-address-fieldset")).toBeFalsy();
expect(queryByTestId("local-address-fieldset")).toBeFalsy();
});
});
describe("for a public space", () => {
const joinRule = JoinRule.Public;
const guestRule = GuestAccess.CanJoin;
const historyRule = HistoryVisibility.Joined;
describe("Access", () => {
it("renders guest access section toggle", async () => {
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
const component = getComponent({ space });
await toggleGuestAccessSection(component);
expect(getGuestAccessToggle(component)).toMatchSnapshot();
});
it("send guest access event on toggle", async () => {
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
const component = getComponent({ space });
await toggleGuestAccessSection(component);
const guestAccessInput = getGuestAccessToggle(component);
expect(guestAccessInput?.getAttribute("aria-checked")).toEqual("true");
fireEvent.click(guestAccessInput!);
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
mockSpaceId,
EventType.RoomGuestAccess,
// toggled off
{ guest_access: GuestAccess.Forbidden },
"",
);
// toggled off
expect(guestAccessInput?.getAttribute("aria-checked")).toEqual("false");
});
it("renders error message when update fails", async () => {
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
(mockMatrixClient.sendStateEvent as jest.Mock).mockRejectedValue({});
const component = getComponent({ space });
await toggleGuestAccessSection(component);
await act(() => {
fireEvent.click(getGuestAccessToggle(component)!);
});
expect(getErrorMessage(component)).toEqual("Failed to update the guest access of this space");
});
it("disables guest access toggle when setting guest access is not allowed", async () => {
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
(space.currentState.maySendStateEvent as jest.Mock).mockReturnValue(false);
const component = getComponent({ space });
await toggleGuestAccessSection(component);
expect(getGuestAccessToggle(component)?.getAttribute("aria-disabled")).toEqual("true");
});
});
describe("Preview", () => {
it("renders preview space toggle", () => {
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
const component = getComponent({ space });
// toggle off because space settings is != WorldReadable
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false");
});
it("updates history visibility on toggle", () => {
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
const component = getComponent({ space });
// toggle off because space settings is != WorldReadable
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false");
fireEvent.click(getHistoryVisibilityToggle(component)!);
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
mockSpaceId,
EventType.RoomHistoryVisibility,
{ history_visibility: HistoryVisibility.WorldReadable },
"",
);
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("true");
});
it("renders error message when history update fails", async () => {
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
(mockMatrixClient.sendStateEvent as jest.Mock).mockRejectedValue({});
const component = getComponent({ space });
await act(() => {
fireEvent.click(getHistoryVisibilityToggle(component)!);
});
expect(getErrorMessage(component)).toEqual("Failed to update the history visibility of this space");
});
it("disables room preview toggle when history visibility changes are not allowed", () => {
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
(space.currentState.maySendStateEvent as jest.Mock).mockReturnValue(false);
const component = getComponent({ space });
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-disabled")).toEqual("true");
});
});
it("renders addresses section", () => {
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
const { getByTestId } = getComponent({ space });
expect(getByTestId("published-address-fieldset")).toBeTruthy();
expect(getByTestId("local-address-fieldset")).toBeTruthy();
});
});
});