mv element.io @types __mocks__/ debian docker module_system/ playwright res src test webapp Dockerfile .dockerignore .eslintignore .stylelintrc.cjs babel.config.cjs recorder-worklet-loader.cjs .modernizr.json components.json config.json config.sample.json package.json project.json tsconfig.json tsconfig.module_system.json jest.config.ts playwright.config.ts webpack.config.ts build_config.sample.yaml apps/web/
mkdir apps/web/scripts
mv scripts/{cleanup.sh,ci_package.sh,copy-res.ts,deploy.py,package.sh} apps/web/scripts
And a couple of gitignore tweaks
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
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 OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { render } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import React from "react";
|
||||
|
||||
import AddExistingToSpaceDialog from "../../../../../src/components/views/dialogs/AddExistingToSpaceDialog";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { mkRoom, mkSpace, stubClient } from "../../../../test-utils";
|
||||
|
||||
describe("<AddExistingToSpaceDialog />", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(Element.prototype, "clientHeight", "get").mockReturnValue(600);
|
||||
});
|
||||
|
||||
it("looks as expected", () => {
|
||||
const client = stubClient();
|
||||
const dialog = renderAddExistingToSpaceDialog(client);
|
||||
expect(dialog.asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should show 'no results' if appropriate", () => {
|
||||
const client = stubClient();
|
||||
const { getByText } = renderAddExistingToSpaceDialog(client);
|
||||
expect(getByText("No results")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not show 'no results' if we have results to show", () => {
|
||||
const client = stubClient();
|
||||
mocked(client.getVisibleRooms).mockReturnValue([
|
||||
mkSpace(client, "!space2:example.com"),
|
||||
mkRoom(client, "!room2:example.com"),
|
||||
]);
|
||||
const { queryByText, getByText } = renderAddExistingToSpaceDialog(client);
|
||||
expect(queryByText("No results")).not.toBeInTheDocument();
|
||||
expect(getByText("!room2:example.com")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("If the feature_dynamic_room_predecessors is not enabled", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
});
|
||||
|
||||
it("Passes through the dynamic predecessor setting", async () => {
|
||||
const client = stubClient();
|
||||
mocked(client.getVisibleRooms).mockClear();
|
||||
renderAddExistingToSpaceDialog(client);
|
||||
expect(client.getVisibleRooms).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("If the feature_dynamic_room_predecessors is enabled", () => {
|
||||
beforeEach(() => {
|
||||
// Turn on feature_dynamic_room_predecessors setting
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(
|
||||
(settingName) => settingName === "feature_dynamic_room_predecessors",
|
||||
);
|
||||
});
|
||||
|
||||
it("Passes through the dynamic predecessor setting", async () => {
|
||||
const client = stubClient();
|
||||
mocked(client.getVisibleRooms).mockClear();
|
||||
renderAddExistingToSpaceDialog(client);
|
||||
expect(client.getVisibleRooms).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function renderAddExistingToSpaceDialog(client: MatrixClient) {
|
||||
const dmRoomMap = new DMRoomMap(client);
|
||||
jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap);
|
||||
const space = mkSpace(client, "!spaceid:example.com");
|
||||
const dialog = render(
|
||||
<AddExistingToSpaceDialog
|
||||
space={space}
|
||||
onCreateRoomClick={jest.fn()}
|
||||
onAddSubspaceClick={jest.fn()}
|
||||
onFinished={jest.fn()}
|
||||
/>,
|
||||
);
|
||||
return dialog;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
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 OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import QuickSettingsButton from "../../../../../src/components/views/spaces/QuickSettingsButton";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||
|
||||
describe("QuickSettingsButton", () => {
|
||||
const roomId = "!room:example.com";
|
||||
|
||||
const renderQuickSettingsButton = () => {
|
||||
render(<QuickSettingsButton isPanelCollapsed={true} />);
|
||||
};
|
||||
|
||||
const getQuickSettingsButton = () => {
|
||||
return screen.getByRole("button", { name: "Quick settings" });
|
||||
};
|
||||
|
||||
const openQuickSettings = async () => {
|
||||
await userEvent.click(getQuickSettingsButton());
|
||||
await screen.findByRole("heading", { name: "Quick settings" });
|
||||
};
|
||||
|
||||
it("should render the quick settings button", () => {
|
||||
renderQuickSettingsButton();
|
||||
expect(getQuickSettingsButton()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the quick settings button in expanded mode", () => {
|
||||
const { asFragment } = render(<QuickSettingsButton isPanelCollapsed={false} />);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("when the quick settings are open", () => {
|
||||
beforeEach(async () => {
|
||||
renderQuickSettingsButton();
|
||||
await openQuickSettings();
|
||||
});
|
||||
|
||||
it("should not render the »Developer tools« button", () => {
|
||||
renderQuickSettingsButton();
|
||||
expect(screen.queryByText("Developer tools")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when developer mode is enabled", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "developerMode");
|
||||
renderQuickSettingsButton();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mocked(SettingsStore.getValue).mockRestore();
|
||||
});
|
||||
|
||||
describe("and no room is viewed", () => {
|
||||
it("should not render the »Developer tools« button", () => {
|
||||
renderQuickSettingsButton();
|
||||
expect(screen.queryByText("Developer tools")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and a room is viewed", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue(roomId);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mocked(SdkContextClass.instance.roomViewStore.getRoomId).mockRestore();
|
||||
});
|
||||
|
||||
describe("and the quick settings are open", () => {
|
||||
beforeEach(async () => {
|
||||
await openQuickSettings();
|
||||
});
|
||||
|
||||
it("should render the »Developer tools« button", () => {
|
||||
expect(screen.getByRole("button", { name: "Developer tools" })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
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, screen, waitFor } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import QuickThemeSwitcher from "../../../../../src/components/views/spaces/QuickThemeSwitcher";
|
||||
import { getOrderedThemes } from "../../../../../src/theme";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
import dis from "../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import { mockPlatformPeg } from "../../../../test-utils/platform";
|
||||
import { useTheme } from "../../../../../src/hooks/useTheme";
|
||||
|
||||
jest.mock("../../../../../src/hooks/useTheme", () => ({
|
||||
useTheme: jest.fn(),
|
||||
}));
|
||||
jest.mock("../../../../../src/theme");
|
||||
jest.mock("../../../../../src/settings/SettingsStore", () => ({
|
||||
setValue: jest.fn(),
|
||||
getValue: jest.fn(),
|
||||
monitorSetting: jest.fn(),
|
||||
watchSetting: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/dispatcher/dispatcher", () => ({
|
||||
dispatch: jest.fn(),
|
||||
register: jest.fn(),
|
||||
}));
|
||||
|
||||
mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) });
|
||||
|
||||
describe("<QuickThemeSwitcher />", () => {
|
||||
const defaultProps = {
|
||||
requestClose: jest.fn(),
|
||||
};
|
||||
const renderComponent = (props = {}) => render(<QuickThemeSwitcher {...defaultProps} {...props} />);
|
||||
|
||||
beforeEach(() => {
|
||||
mocked(getOrderedThemes)
|
||||
.mockClear()
|
||||
.mockReturnValue([
|
||||
{ id: "light", name: "Light" },
|
||||
{ id: "dark", name: "Dark" },
|
||||
]);
|
||||
|
||||
mocked(useTheme).mockClear().mockReturnValue({
|
||||
theme: "light",
|
||||
systemThemeActivated: false,
|
||||
});
|
||||
mocked(SettingsStore).setValue.mockClear().mockResolvedValue();
|
||||
mocked(dis).dispatch.mockClear();
|
||||
});
|
||||
|
||||
const selectFromDropdown = async (getByTextArg: RegExp | string) => {
|
||||
const dropdown = screen.getByRole("button", { name: "Theme" });
|
||||
await userEvent.click(dropdown);
|
||||
await waitFor(() => {
|
||||
expect(dropdown).toHaveAttribute("aria-expanded", "true");
|
||||
});
|
||||
await userEvent.click(screen.getByText(getByTextArg));
|
||||
return waitFor(() => {
|
||||
expect(dropdown).toHaveAttribute("aria-expanded", "false");
|
||||
});
|
||||
};
|
||||
|
||||
it("renders dropdown correctly when light theme is selected", () => {
|
||||
renderComponent();
|
||||
expect(screen.getByText("Light")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders dropdown correctly when use system theme is truthy", () => {
|
||||
mocked(useTheme).mockClear().mockReturnValue({
|
||||
theme: "light",
|
||||
systemThemeActivated: true,
|
||||
});
|
||||
renderComponent();
|
||||
expect(screen.getByText("Match system")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("updates settings when match system is selected", async () => {
|
||||
const requestClose = jest.fn();
|
||||
renderComponent({ requestClose });
|
||||
|
||||
await selectFromDropdown(/match system/i);
|
||||
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledTimes(1);
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith("use_system_theme", null, SettingLevel.DEVICE, true);
|
||||
|
||||
expect(dis.dispatch).not.toHaveBeenCalled();
|
||||
expect(requestClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("updates settings when a theme is selected", async () => {
|
||||
// ie not match system
|
||||
const requestClose = jest.fn();
|
||||
renderComponent({ requestClose });
|
||||
|
||||
await selectFromDropdown(/dark/i);
|
||||
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith("use_system_theme", null, SettingLevel.DEVICE, false);
|
||||
expect(SettingsStore.setValue).toHaveBeenCalledWith("theme", null, SettingLevel.DEVICE, "dark");
|
||||
|
||||
expect(dis.dispatch).toHaveBeenCalledWith({ action: Action.RecheckTheme, forceTheme: "dark" });
|
||||
expect(requestClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rechecks theme when setting theme fails", async () => {
|
||||
mocked(SettingsStore.setValue).mockRejectedValue("oops");
|
||||
const requestClose = jest.fn();
|
||||
renderComponent({ requestClose });
|
||||
|
||||
await selectFromDropdown(/match system/i);
|
||||
|
||||
expect(dis.dispatch).toHaveBeenCalledWith({ action: Action.RecheckTheme });
|
||||
expect(requestClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -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,192 @@
|
||||
/*
|
||||
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, screen, fireEvent, act, cleanup } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { type MatrixClient, type Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { MetaSpace, type SpaceKey } from "../../../../../src/stores/spaces";
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../../src/settings/UIFeature";
|
||||
import { mkStubRoom, wrapInMatrixClientContext, wrapInSdkContext } from "../../../../test-utils";
|
||||
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||
import SpaceStore from "../../../../../src/stores/spaces/SpaceStore";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { type SpaceNotificationState } from "../../../../../src/stores/notifications/SpaceNotificationState";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import UnwrappedSpacePanel from "../../../../../src/components/views/spaces/SpacePanel";
|
||||
|
||||
// DND test utilities based on
|
||||
// https://github.com/colinrobertbrooks/react-beautiful-dnd-test-utils/issues/18#issuecomment-1373388693
|
||||
enum Keys {
|
||||
SPACE = 32,
|
||||
ARROW_LEFT = 37,
|
||||
ARROW_UP = 38,
|
||||
ARROW_RIGHT = 39,
|
||||
ARROW_DOWN = 40,
|
||||
}
|
||||
|
||||
enum DragDirection {
|
||||
LEFT = Keys.ARROW_LEFT,
|
||||
UP = Keys.ARROW_UP,
|
||||
RIGHT = Keys.ARROW_RIGHT,
|
||||
DOWN = Keys.ARROW_DOWN,
|
||||
}
|
||||
|
||||
// taken from https://github.com/hello-pangea/dnd/blob/main/test/unit/integration/util/controls.ts#L20
|
||||
const createTransitionEndEvent = (): Event => {
|
||||
const event = new Event("transitionend", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}) as TransitionEvent;
|
||||
|
||||
// cheating and adding property to event as
|
||||
// TransitionEvent constructor does not exist.
|
||||
// This is needed because of the following check
|
||||
// https://github.com/atlassian/react-beautiful-dnd/blob/master/src/view/draggable/draggable.jsx#L130
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(event as any).propertyName = "transform";
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
const pickUp = async (element: HTMLElement) => {
|
||||
fireEvent.keyDown(element, {
|
||||
keyCode: Keys.SPACE,
|
||||
});
|
||||
await screen.findByText(/You have lifted an item/i);
|
||||
|
||||
act(() => {
|
||||
jest.runOnlyPendingTimers();
|
||||
});
|
||||
};
|
||||
|
||||
const move = async (element: HTMLElement, direction: DragDirection) => {
|
||||
fireEvent.keyDown(element, {
|
||||
keyCode: direction,
|
||||
});
|
||||
await screen.findByText(/(You have moved the item | has been combined with)/i);
|
||||
};
|
||||
|
||||
const drop = async (element: HTMLElement) => {
|
||||
fireEvent.keyDown(element, {
|
||||
keyCode: Keys.SPACE,
|
||||
});
|
||||
fireEvent(element.parentElement!, createTransitionEndEvent());
|
||||
|
||||
await screen.findByText(/You have dropped the item/i);
|
||||
};
|
||||
|
||||
jest.mock("../../../../../src/stores/spaces/SpaceStore", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const EventEmitter = require("events");
|
||||
class MockSpaceStore extends EventEmitter {
|
||||
invitedSpaces: SpaceKey[] = [];
|
||||
enabledMetaSpaces: MetaSpace[] = [];
|
||||
spacePanelSpaces: string[] = [];
|
||||
activeSpace: SpaceKey = "!space1";
|
||||
getChildSpaces = () => [] as Room[];
|
||||
getNotificationState = () => null as SpaceNotificationState | null;
|
||||
setActiveSpace = jest.fn();
|
||||
moveRootSpace = jest.fn();
|
||||
}
|
||||
return {
|
||||
instance: new MockSpaceStore(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock("../../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("<SpacePanel />", () => {
|
||||
const mockClient = {
|
||||
getUserId: jest.fn().mockReturnValue("@test:test"),
|
||||
getSafeUserId: jest.fn().mockReturnValue("@test:test"),
|
||||
mxcUrlToHttp: jest.fn(),
|
||||
getRoom: jest.fn(),
|
||||
isGuest: jest.fn(),
|
||||
getAccountData: jest.fn(),
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
isVersionSupported: jest.fn().mockResolvedValue(true),
|
||||
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(false),
|
||||
} as unknown as MatrixClient;
|
||||
const SpacePanel = wrapInSdkContext(wrapInMatrixClientContext(UnwrappedSpacePanel), SdkContextClass.instance);
|
||||
|
||||
beforeAll(() => {
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
|
||||
jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(mockClient);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
SpaceStore.instance.enabledMetaSpaces.push(
|
||||
MetaSpace.Home,
|
||||
MetaSpace.Favourites,
|
||||
MetaSpace.People,
|
||||
MetaSpace.Orphans,
|
||||
MetaSpace.VideoRooms,
|
||||
);
|
||||
mocked(shouldShowComponent).mockClear().mockReturnValue(true);
|
||||
});
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("should show all activated MetaSpaces in the correct order", async () => {
|
||||
const originalGetValue = SettingsStore.getValue;
|
||||
const spySettingsStore = jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => {
|
||||
return setting === "feature_video_rooms" ? true : originalGetValue(setting);
|
||||
});
|
||||
const renderResult = render(<SpacePanel />);
|
||||
expect(renderResult.asFragment()).toMatchSnapshot();
|
||||
spySettingsStore.mockRestore();
|
||||
});
|
||||
|
||||
describe("create new space button", () => {
|
||||
it("renders create space button when UIComponent.CreateSpaces component should be shown", () => {
|
||||
render(<SpacePanel />);
|
||||
screen.getByTestId("create-space-button");
|
||||
});
|
||||
|
||||
it("does not render create space button when UIComponent.CreateSpaces component should not be shown", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(false);
|
||||
render(<SpacePanel />);
|
||||
expect(shouldShowComponent).toHaveBeenCalledWith(UIComponent.CreateSpaces);
|
||||
expect(screen.queryByTestId("create-space-button")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("opens context menu on create space button click", () => {
|
||||
render(<SpacePanel />);
|
||||
fireEvent.click(screen.getByTestId("create-space-button"));
|
||||
screen.getByTestId("create-space-button");
|
||||
});
|
||||
});
|
||||
|
||||
it("should allow rearranging via drag and drop", async () => {
|
||||
(SpaceStore.instance.spacePanelSpaces as any) = [
|
||||
mkStubRoom("!room1:server", "Room 1", mockClient),
|
||||
mkStubRoom("!room2:server", "Room 2", mockClient),
|
||||
mkStubRoom("!room3:server", "Room 3", mockClient),
|
||||
];
|
||||
DMRoomMap.makeShared(mockClient);
|
||||
jest.useFakeTimers();
|
||||
|
||||
const { getByLabelText } = render(<SpacePanel />);
|
||||
|
||||
const room1 = getByLabelText("Room 1");
|
||||
await pickUp(room1);
|
||||
await move(room1, DragDirection.DOWN);
|
||||
await drop(room1);
|
||||
|
||||
expect(SpaceStore.instance.moveRootSpace).toHaveBeenCalledWith(0, 1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
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 { mocked } from "jest-mock";
|
||||
import { secureRandomString } from "matrix-js-sdk/src/randomstring";
|
||||
import { act, fireEvent, render, type RenderResult } from "jest-matrix-react";
|
||||
import {
|
||||
EventType,
|
||||
type MatrixClient,
|
||||
type Room,
|
||||
GuestAccess,
|
||||
HistoryVisibility,
|
||||
JoinRule,
|
||||
Visibility,
|
||||
} 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("<SpaceSettingsVisibilityTab />", () => {
|
||||
const mockMatrixClient = createTestClient() as MatrixClient;
|
||||
mocked(mockMatrixClient.isVersionSupported).mockImplementation(async (v) => v === "v1.4");
|
||||
|
||||
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);
|
||||
mocked(client.getRoom).mockImplementation((roomId) => (roomId === mockSpaceId ? space : null));
|
||||
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(<SpaceSettingsVisibilityTab {...defaultProps} {...props} />);
|
||||
};
|
||||
|
||||
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(secureRandomString).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;
|
||||
mocked(mockMatrixClient.getRoomDirectoryVisibility).mockResolvedValue({ visibility: Visibility.Public });
|
||||
|
||||
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).toBeChecked();
|
||||
|
||||
fireEvent.click(guestAccessInput!);
|
||||
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||
mockSpaceId,
|
||||
EventType.RoomGuestAccess,
|
||||
// toggled off
|
||||
{ guest_access: GuestAccess.Forbidden },
|
||||
"",
|
||||
);
|
||||
|
||||
// toggled off
|
||||
expect(guestAccessInput).not.toBeChecked();
|
||||
});
|
||||
|
||||
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)).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
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)).not.toBeChecked();
|
||||
});
|
||||
|
||||
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)).not.toBeChecked();
|
||||
|
||||
fireEvent.click(getHistoryVisibilityToggle(component)!);
|
||||
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||
mockSpaceId,
|
||||
EventType.RoomHistoryVisibility,
|
||||
{ history_visibility: HistoryVisibility.WorldReadable },
|
||||
"",
|
||||
);
|
||||
|
||||
expect(getHistoryVisibilityToggle(component)).toBeChecked();
|
||||
});
|
||||
|
||||
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)).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
it("renders addresses section with publish toggle", async () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
|
||||
const { findByLabelText, getByTestId, asFragment } = getComponent({ space });
|
||||
|
||||
expect(getByTestId("published-address-fieldset")).toBeTruthy();
|
||||
expect(getByTestId("local-address-fieldset")).toBeTruthy();
|
||||
await expect(
|
||||
findByLabelText("Publish this room to the public in matrix.org's room directory?"),
|
||||
).resolves.toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
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 { fireEvent, getByTestId, render } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { mkRoom, stubClient } from "../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import { SpaceItem, SpaceButton } from "../../../../../src/components/views/spaces/SpaceTreeLevel";
|
||||
import { MetaSpace, type SpaceKey } from "../../../../../src/stores/spaces";
|
||||
import SpaceStore from "../../../../../src/stores/spaces/SpaceStore";
|
||||
import { StaticNotificationState } from "../../../../../src/stores/notifications/StaticNotificationState";
|
||||
import { NotificationLevel } from "../../../../../src/stores/notifications/NotificationLevel";
|
||||
|
||||
jest.mock("../../../../../src/stores/spaces/SpaceStore", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const EventEmitter = require("events");
|
||||
class MockSpaceStore extends EventEmitter {
|
||||
activeSpace: SpaceKey = "!space1";
|
||||
setActiveSpace = jest.fn();
|
||||
getChildSpaces = jest.fn();
|
||||
getNotificationState = jest.fn();
|
||||
}
|
||||
|
||||
return { instance: new MockSpaceStore() };
|
||||
});
|
||||
|
||||
describe("SpaceButton", () => {
|
||||
stubClient();
|
||||
const space = mkRoom(MatrixClientPeg.safeGet(), "!1:example.org");
|
||||
DMRoomMap.makeShared(MatrixClientPeg.safeGet());
|
||||
|
||||
const dispatchSpy = jest.spyOn(defaultDispatcher, "dispatch");
|
||||
|
||||
afterEach(jest.clearAllMocks);
|
||||
|
||||
describe("real space", () => {
|
||||
it("activates the space on click", () => {
|
||||
const { container } = render(
|
||||
<SpaceButton
|
||||
space={space}
|
||||
selected={false}
|
||||
label="My space"
|
||||
data-testid="create-space-button"
|
||||
size="32px"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(SpaceStore.instance.setActiveSpace).not.toHaveBeenCalled();
|
||||
fireEvent.click(getByTestId(container, "create-space-button"));
|
||||
expect(SpaceStore.instance.setActiveSpace).toHaveBeenCalledWith("!1:example.org");
|
||||
});
|
||||
|
||||
it("navigates to the space home on click if already active", () => {
|
||||
const { container } = render(
|
||||
<SpaceButton
|
||||
space={space}
|
||||
selected={true}
|
||||
label="My space"
|
||||
data-testid="create-space-button"
|
||||
size="32px"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(dispatchSpy).not.toHaveBeenCalled();
|
||||
fireEvent.click(getByTestId(container, "create-space-button"));
|
||||
expect(dispatchSpy).toHaveBeenCalledWith({ action: Action.ViewRoom, room_id: "!1:example.org" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("metaspace", () => {
|
||||
it("activates the metaspace on click", () => {
|
||||
const { container } = render(
|
||||
<SpaceButton
|
||||
spaceKey={MetaSpace.People}
|
||||
selected={false}
|
||||
label="People"
|
||||
data-testid="create-space-button"
|
||||
size="32px"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(SpaceStore.instance.setActiveSpace).not.toHaveBeenCalled();
|
||||
fireEvent.click(getByTestId(container, "create-space-button"));
|
||||
expect(SpaceStore.instance.setActiveSpace).toHaveBeenCalledWith(MetaSpace.People);
|
||||
});
|
||||
|
||||
it("does nothing on click if already active", () => {
|
||||
const { container } = render(
|
||||
<SpaceButton
|
||||
spaceKey={MetaSpace.People}
|
||||
selected={true}
|
||||
label="People"
|
||||
data-testid="create-space-button"
|
||||
size="32px"
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(getByTestId(container, "create-space-button"));
|
||||
expect(dispatchSpy).not.toHaveBeenCalled();
|
||||
// Re-activating the metaspace is a no-op
|
||||
expect(SpaceStore.instance.setActiveSpace).toHaveBeenCalledWith(MetaSpace.People);
|
||||
});
|
||||
|
||||
it("should render notificationState if one is provided", () => {
|
||||
const notificationState = new StaticNotificationState(null, 8, NotificationLevel.Notification);
|
||||
|
||||
const { container, asFragment } = render(
|
||||
<SpaceButton
|
||||
spaceKey={MetaSpace.People}
|
||||
selected={true}
|
||||
label="People"
|
||||
data-testid="create-space-button"
|
||||
notificationState={notificationState}
|
||||
size="32px"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.querySelector(".mx_NotificationBadge_count")).toHaveTextContent("8");
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("SpaceItem", () => {
|
||||
const cli = stubClient();
|
||||
const space = mkRoom(cli, "!1:example.org");
|
||||
space.name = "Root Space";
|
||||
const subspace = mkRoom(cli, "!2:example.org");
|
||||
subspace.name = "Subspace";
|
||||
|
||||
it("should render a space with subspaces", () => {
|
||||
mocked(SpaceStore.instance.getChildSpaces).mockImplementation((spaceId) =>
|
||||
spaceId === space.roomId ? [subspace] : [],
|
||||
);
|
||||
|
||||
const { asFragment, queryByText, getByLabelText } = render(<SpaceItem space={space} activeSpaces={[]} />);
|
||||
|
||||
expect(queryByText("Root Space")).toBeVisible();
|
||||
expect(queryByText("Subspace")).toBeNull();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(getByLabelText("Expand"));
|
||||
expect(queryByText("Root Space")).toBeVisible();
|
||||
expect(queryByText("Subspace")).toBeVisible();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
* Copyright 2024 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, { type ComponentProps } from "react";
|
||||
import { getByText, render, screen } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { NotificationCountType, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { ThreadsActivityCentre } from "../../../../../src/components/views/spaces/threads-activity-centre";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import { populateThread } from "../../../../test-utils/threads";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
|
||||
describe("ThreadsActivityCentre", () => {
|
||||
const getTACButton = () => {
|
||||
return screen.getByRole("button", { name: "Threads" });
|
||||
};
|
||||
|
||||
const getTACMenu = () => {
|
||||
return screen.getByRole("menu");
|
||||
};
|
||||
|
||||
const getTACDescription = (container: ReturnType<typeof render>["container"]) => {
|
||||
return container.querySelector(".mx_ThreadsActivityCentreButton_Text");
|
||||
};
|
||||
|
||||
const renderTAC = (props?: ComponentProps<typeof ThreadsActivityCentre>) => {
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<ThreadsActivityCentre {...props} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
const cli = stubClient();
|
||||
cli.supportsThreads = () => true;
|
||||
|
||||
const roomWithActivity = new Room("!room:server", cli, cli.getSafeUserId(), {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
roomWithActivity.name = "Just activity";
|
||||
|
||||
const roomWithNotif = new Room("!room2:server", cli, cli.getSafeUserId(), {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
roomWithNotif.name = "A notification";
|
||||
|
||||
const roomWithHighlight = new Room("!room3:server", cli, cli.getSafeUserId(), {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
roomWithHighlight.name = "This is a real highlight";
|
||||
|
||||
const getDefaultThreadArgs = (room: Room) => ({
|
||||
room: room,
|
||||
client: cli,
|
||||
authorId: "@foo:bar",
|
||||
participantUserIds: ["@fee:bar"],
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(cli);
|
||||
jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(cli);
|
||||
|
||||
const dmRoomMap = new DMRoomMap(cli);
|
||||
jest.spyOn(dmRoomMap, "getUserIdForRoomId");
|
||||
jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap);
|
||||
|
||||
await populateThread(getDefaultThreadArgs(roomWithActivity));
|
||||
|
||||
const notifThreadInfo = await populateThread(getDefaultThreadArgs(roomWithNotif));
|
||||
roomWithNotif.setThreadUnreadNotificationCount(notifThreadInfo.thread.id, NotificationCountType.Total, 1);
|
||||
|
||||
const highlightThreadInfo = await populateThread({
|
||||
...getDefaultThreadArgs(roomWithHighlight),
|
||||
// timestamp
|
||||
ts: 5,
|
||||
});
|
||||
roomWithHighlight.setThreadUnreadNotificationCount(
|
||||
highlightThreadInfo.thread.id,
|
||||
NotificationCountType.Highlight,
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it("should render the threads activity centre button", async () => {
|
||||
renderTAC();
|
||||
expect(getTACButton()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the threads activity centre button and the display label", async () => {
|
||||
const { container } = renderTAC({ displayButtonLabel: true });
|
||||
expect(getTACButton()).toBeInTheDocument();
|
||||
expect(getTACDescription(container)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the threads activity centre menu when the button is clicked", async () => {
|
||||
renderTAC();
|
||||
await userEvent.click(getTACButton());
|
||||
expect(getTACMenu()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not render a room with a activity in the TAC", async () => {
|
||||
cli.getVisibleRooms = jest.fn().mockReturnValue([roomWithActivity]);
|
||||
renderTAC();
|
||||
await userEvent.click(getTACButton());
|
||||
|
||||
// We should not render the room with activity
|
||||
expect(() => screen.getAllByRole("menuitem")).toThrow();
|
||||
});
|
||||
|
||||
it("should render a room with a regular notification in the TAC", async () => {
|
||||
cli.getVisibleRooms = jest.fn().mockReturnValue([roomWithNotif]);
|
||||
renderTAC();
|
||||
await userEvent.click(getTACButton());
|
||||
|
||||
const tacRows = screen.getAllByRole("menuitem");
|
||||
expect(tacRows.length).toEqual(1);
|
||||
|
||||
getByText(tacRows[0], "A notification");
|
||||
expect(tacRows[0].getElementsByClassName("mx_NotificationBadge_level_notification").length).toEqual(1);
|
||||
});
|
||||
|
||||
it("should render a room with a highlight notification in the TAC", async () => {
|
||||
cli.getVisibleRooms = jest.fn().mockReturnValue([roomWithHighlight]);
|
||||
renderTAC();
|
||||
await userEvent.click(getTACButton());
|
||||
|
||||
const tacRows = screen.getAllByRole("menuitem");
|
||||
expect(tacRows.length).toEqual(1);
|
||||
|
||||
getByText(tacRows[0], "This is a real highlight");
|
||||
expect(tacRows[0].getElementsByClassName("mx_NotificationBadge_level_highlight").length).toEqual(1);
|
||||
});
|
||||
|
||||
it("renders notifications matching the snapshot", async () => {
|
||||
cli.getVisibleRooms = jest.fn().mockReturnValue([roomWithHighlight, roomWithNotif, roomWithActivity]);
|
||||
renderTAC();
|
||||
await userEvent.click(getTACButton());
|
||||
|
||||
expect(screen.getByRole("menu")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display a caption when no threads are unread", async () => {
|
||||
cli.getVisibleRooms = jest.fn().mockReturnValue([]);
|
||||
renderTAC();
|
||||
await userEvent.click(getTACButton());
|
||||
|
||||
expect(screen.getByRole("menu").getElementsByClassName("mx_ThreadsActivityCentre_emptyCaption").length).toEqual(
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it("should match snapshot when empty", async () => {
|
||||
cli.getVisibleRooms = jest.fn().mockReturnValue([]);
|
||||
renderTAC();
|
||||
await userEvent.click(getTACButton());
|
||||
|
||||
expect(screen.getByRole("menu")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should order the room with the same notification level by most recent", async () => {
|
||||
// Generate two new rooms with threads
|
||||
const secondRoomWithHighlight = new Room("!room4:server", cli, cli.getSafeUserId(), {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
secondRoomWithHighlight.name = "This is a second real highlight";
|
||||
|
||||
const secondHighlightThreadInfo = await populateThread({
|
||||
...getDefaultThreadArgs(secondRoomWithHighlight),
|
||||
// timestamp
|
||||
ts: 1,
|
||||
});
|
||||
secondRoomWithHighlight.setThreadUnreadNotificationCount(
|
||||
secondHighlightThreadInfo.thread.id,
|
||||
NotificationCountType.Highlight,
|
||||
1,
|
||||
);
|
||||
|
||||
const thirdRoomWithHighlight = new Room("!room5:server", cli, cli.getSafeUserId(), {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
thirdRoomWithHighlight.name = "This is a third real highlight";
|
||||
|
||||
const thirdHighlightThreadInfo = await populateThread({
|
||||
...getDefaultThreadArgs(thirdRoomWithHighlight),
|
||||
// timestamp
|
||||
ts: 7,
|
||||
});
|
||||
thirdRoomWithHighlight.setThreadUnreadNotificationCount(
|
||||
thirdHighlightThreadInfo.thread.id,
|
||||
NotificationCountType.Highlight,
|
||||
1,
|
||||
);
|
||||
|
||||
cli.getVisibleRooms = jest
|
||||
.fn()
|
||||
.mockReturnValue([roomWithHighlight, secondRoomWithHighlight, thirdRoomWithHighlight]);
|
||||
|
||||
renderTAC();
|
||||
await userEvent.click(getTACButton());
|
||||
|
||||
// The room should be ordered by the most recent thread
|
||||
// thirdHighlightThreadInfo (timestamp 7) > highlightThreadInfo (timestamp 5) > secondHighlightThreadInfo (timestamp 1)
|
||||
expect(screen.getByRole("menu")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should block Ctrl/CMD + k shortcut", async () => {
|
||||
cli.getVisibleRooms = jest.fn().mockReturnValue([roomWithHighlight]);
|
||||
|
||||
const keyDownHandler = jest.fn();
|
||||
render(
|
||||
<div
|
||||
onKeyDown={(evt) => {
|
||||
keyDownHandler(evt.key, evt.ctrlKey);
|
||||
}}
|
||||
>
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<ThreadsActivityCentre />
|
||||
</MatrixClientContext.Provider>
|
||||
</div>,
|
||||
);
|
||||
await userEvent.click(getTACButton());
|
||||
|
||||
// CTRL/CMD + k should be blocked
|
||||
await userEvent.keyboard("{Control>}k{/Control}");
|
||||
expect(keyDownHandler).not.toHaveBeenCalledWith("k", true);
|
||||
|
||||
// Sanity test
|
||||
await userEvent.keyboard("{Control>}a{/Control}");
|
||||
expect(keyDownHandler).toHaveBeenCalledWith("a", true);
|
||||
});
|
||||
});
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`<AddExistingToSpaceDialog /> looks as expected 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_AddExistingToSpace"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_AddExistingToSpaceDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
<div
|
||||
class="mx_SubspaceSelector"
|
||||
>
|
||||
<span
|
||||
aria-label="Avatar"
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="square"
|
||||
style="--cpd-avatar-size: 40px;"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="_image_zysgz_43"
|
||||
data-type="square"
|
||||
height="40px"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
src="http://this.is.a.url/avatar.url/room.png"
|
||||
width="40px"
|
||||
/>
|
||||
</span>
|
||||
<div>
|
||||
<h1>
|
||||
Add existing rooms
|
||||
</h1>
|
||||
<div
|
||||
class="mx_SubspaceSelector_onlySpace"
|
||||
>
|
||||
!spaceid:example.com
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AddExistingToSpace"
|
||||
>
|
||||
<div
|
||||
class="mx_SearchBox mx_textinput"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="mx_textinput_icon mx_textinput_search mx_textinput_icon mx_textinput_search"
|
||||
data-testid="searchbox-input"
|
||||
placeholder="Search for rooms"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_SearchBox_closeButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
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.414"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar mx_AddExistingToSpace_content"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="mx_AddExistingToSpace_noResults"
|
||||
>
|
||||
No results
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AddExistingToSpace_footer"
|
||||
>
|
||||
<span>
|
||||
<div>
|
||||
Want to add a new room instead?
|
||||
</div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Create a new room
|
||||
</div>
|
||||
</span>
|
||||
<div
|
||||
aria-disabled="true"
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary mx_AccessibleButton_disabled"
|
||||
disabled=""
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Add
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
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.414"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`QuickSettingsButton should render the quick settings button in expanded mode 1`] = `
|
||||
<DocumentFragment>
|
||||
<button
|
||||
aria-expanded="true"
|
||||
aria-label="Quick settings"
|
||||
class="_icon-button_1215g_8 mx_QuickSettingsButton expanded"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12.731 2C13.432 2 14 2.568 14 3.269c0 .578.396 1.074.935 1.286q.128.052.253.106c.531.23 1.162.16 1.572-.25a1.27 1.27 0 0 1 1.794 0l1.034 1.035a1.27 1.27 0 0 1 0 1.794c-.41.41-.48 1.04-.248 1.572l.105.253c.212.539.708.935 1.286.935.701 0 1.269.568 1.269 1.269v1.462c0 .701-.568 1.269-1.269 1.269-.578 0-1.074.396-1.287.935q-.05.128-.104.253c-.232.531-.161 1.162.248 1.572a1.27 1.27 0 0 1 0 1.794l-1.034 1.034a1.27 1.27 0 0 1-1.794 0c-.41-.41-1.04-.48-1.572-.248a8 8 0 0 1-.253.105c-.539.212-.935.708-.935 1.286 0 .701-.568 1.269-1.269 1.269H11.27c-.702 0-1.27-.568-1.27-1.269 0-.578-.396-1.074-.935-1.287a8 8 0 0 1-.253-.104c-.531-.232-1.162-.161-1.572.248a1.27 1.27 0 0 1-1.794 0l-1.034-1.034a1.27 1.27 0 0 1 0-1.794c.41-.41.48-1.04.249-1.572a8 8 0 0 1-.106-.253C4.343 14.396 3.847 14 3.27 14 2.568 14 2 13.432 2 12.731V11.27c0-.702.568-1.27 1.269-1.27.578 0 1.074-.396 1.286-.935q.052-.128.106-.253c.23-.531.16-1.162-.25-1.572a1.27 1.27 0 0 1 0-1.794l1.035-1.034a1.27 1.27 0 0 1 1.794 0c.41.41 1.04.48 1.572.249a8 8 0 0 1 .253-.106c.539-.212.935-.708.935-1.286C10 2.568 10.568 2 11.269 2zM12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-regular_6v6n8_50 mx_QuickSettingsButton_label"
|
||||
title="Settings"
|
||||
>
|
||||
Settings
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`<SpaceCreateMenu /> should render 1`] = `<DocumentFragment />`;
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`<SpacePanel /> should show all activated MetaSpaces in the correct order 1`] = `
|
||||
<DocumentFragment>
|
||||
<nav
|
||||
aria-label="Spaces"
|
||||
class="mx_SpacePanel collapsed newUi"
|
||||
>
|
||||
<div
|
||||
class="mx_UserMenu"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="User menu"
|
||||
class="mx_AccessibleButton mx_UserMenu_contextMenuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_UserMenu_userAvatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar mx_UserMenu_userAvatar_BaseAvatar _avatar-imageless_zysgz_55"
|
||||
data-color="5"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
t
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Expand"
|
||||
class="mx_AccessibleButton mx_SpacePanel_toggleCollapse"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
aria-label="Spaces"
|
||||
class="mx_AutoHideScrollbar mx_SpaceTreeLevel"
|
||||
data-rbd-droppable-context-id="0"
|
||||
data-rbd-droppable-id="top-level-spaces"
|
||||
role="tree"
|
||||
tabindex="-1"
|
||||
>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem collapsed"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Home"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_narrow mx_SpaceButton_withIcon"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarPlaceholder"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m12.971 3.54 7 3.889A2 2 0 0 1 21 9.177V19a2 2 0 0 1-2 2h-4v-9H9v9H5a2 2 0 0 1-2-2V9.177a2 2 0 0 1 1.029-1.748l7-3.89a2 2 0 0 1 1.942 0"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Options"
|
||||
class="mx_AccessibleButton mx_SpaceButton_menuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem collapsed"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Favourites"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_narrow mx_SpaceButton_withIcon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarPlaceholder"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m12.897 2.817 2.336 4.733 5.223.76a1 1 0 0 1 .555 1.705L17.23 13.7l.892 5.202a1 1 0 0 1-1.45 1.054L12 17.5l-4.672 2.456a1 1 0 0 1-1.451-1.054l.892-5.202-3.78-3.685a1 1 0 0 1 .555-1.706l5.223-.759 2.336-4.733a1 1 0 0 1 1.794 0"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem collapsed"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="People"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_narrow mx_SpaceButton_withIcon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarPlaceholder"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 15q-1.65 0-2.825-1.175T8 11t1.175-2.825T12 7t2.825 1.175T16 11t-1.175 2.825T12 15"
|
||||
/>
|
||||
<path
|
||||
d="M19.528 18.583A9.96 9.96 0 0 0 22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 2.52.933 4.824 2.472 6.583A9.98 9.98 0 0 0 12 22a9.98 9.98 0 0 0 7.528-3.417M8.75 16.388q-1.373.332-2.709.95a8 8 0 1 1 11.918 0 14.7 14.7 0 0 0-2.709-.95A13.8 13.8 0 0 0 12 16q-1.65 0-3.25.387"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem collapsed"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Other rooms"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_narrow mx_SpaceButton_withIcon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarPlaceholder"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m8.566 17-.944 4.094q-.086.406-.372.656t-.687.25q-.543 0-.887-.469a1.18 1.18 0 0 1-.2-1.031l.801-3.5H3.158q-.572 0-.916-.484a1.27 1.27 0 0 1-.2-1.078 1.12 1.12 0 0 1 1.116-.938H6.85l1.145-5h-3.12q-.57 0-.915-.484a1.27 1.27 0 0 1-.2-1.078A1.12 1.12 0 0 1 4.875 7h3.691l.945-4.094q.085-.406.372-.656.286-.25.686-.25.544 0 .887.469.345.468.2 1.031l-.8 3.5h4.578l.944-4.094q.085-.406.372-.656.286-.25.687-.25.543 0 .887.469t.2 1.031L17.723 7h3.119q.573 0 .916.484.343.485.2 1.079a1.12 1.12 0 0 1-1.116.937H17.15l-1.145 5h3.12q.57 0 .915.484.343.485.2 1.079a1.12 1.12 0 0 1-1.116.937h-3.691l-.944 4.094q-.087.406-.373.656t-.686.25q-.544 0-.887-.469a1.18 1.18 0 0 1-.2-1.031l.8-3.5zm.573-2.5h4.578l1.144-5h-4.578z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem collapsed"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Conferences"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_narrow mx_SpaceButton_withIcon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarPlaceholder"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem mx_SpaceItem_new collapsed"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Create a space"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_new mx_SpaceButton_narrow mx_SpaceButton_withIcon"
|
||||
data-testid="create-space-button"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarPlaceholder"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_icon"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11 13H6a.97.97 0 0 1-.713-.287A.97.97 0 0 1 5 12q0-.424.287-.713A.97.97 0 0 1 6 11h5V6q0-.424.287-.713A.97.97 0 0 1 12 5q.424 0 .713.287Q13 5.576 13 6v5h5q.424 0 .712.287.288.288.288.713 0 .424-.288.713A.97.97 0 0 1 18 13h-5v5q0 .424-.287.712A.97.97 0 0 1 12 19a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 18z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_container"
|
||||
>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Threads"
|
||||
aria-labelledby="_r_12_"
|
||||
class="_icon-button_1215g_8 mx_ThreadsActivityCentreButton"
|
||||
data-kind="primary"
|
||||
data-state="closed"
|
||||
id="radix-_r_10_"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
class="mx_ThreadsActivityCentreButton_Icon"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4 3h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-2.293 2.293c-.63.63-1.707.184-1.707-.707V5a2 2 0 0 1 2-2m3 7h10q.424 0 .712-.287A.97.97 0 0 0 18 9a.97.97 0 0 0-.288-.713A.97.97 0 0 0 17 8H7a.97.97 0 0 0-.713.287A.97.97 0 0 0 6 9q0 .424.287.713Q6.576 10 7 10m0 4h6q.424 0 .713-.287A.97.97 0 0 0 14 13a.97.97 0 0 0-.287-.713A.97.97 0 0 0 13 12H7a.97.97 0 0 0-.713.287A.97.97 0 0 0 6 13q0 .424.287.713Q6.576 14 7 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
aria-expanded="false"
|
||||
aria-label="Quick settings"
|
||||
aria-labelledby="_r_17_"
|
||||
class="_icon-button_1215g_8 mx_QuickSettingsButton"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
title="Quick settings"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_147l5_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12.731 2C13.432 2 14 2.568 14 3.269c0 .578.396 1.074.935 1.286q.128.052.253.106c.531.23 1.162.16 1.572-.25a1.27 1.27 0 0 1 1.794 0l1.034 1.035a1.27 1.27 0 0 1 0 1.794c-.41.41-.48 1.04-.248 1.572l.105.253c.212.539.708.935 1.286.935.701 0 1.269.568 1.269 1.269v1.462c0 .701-.568 1.269-1.269 1.269-.578 0-1.074.396-1.287.935q-.05.128-.104.253c-.232.531-.161 1.162.248 1.572a1.27 1.27 0 0 1 0 1.794l-1.034 1.034a1.27 1.27 0 0 1-1.794 0c-.41-.41-1.04-.48-1.572-.248a8 8 0 0 1-.253.105c-.539.212-.935.708-.935 1.286 0 .701-.568 1.269-1.269 1.269H11.27c-.702 0-1.27-.568-1.27-1.269 0-.578-.396-1.074-.935-1.287a8 8 0 0 1-.253-.104c-.531-.232-1.162-.161-1.572.248a1.27 1.27 0 0 1-1.794 0l-1.034-1.034a1.27 1.27 0 0 1 0-1.794c.41-.41.48-1.04.249-1.572a8 8 0 0 1-.106-.253C4.343 14.396 3.847 14 3.27 14 2.568 14 2 13.432 2 12.731V11.27c0-.702.568-1.27 1.269-1.27.578 0 1.074-.396 1.286-.935q.052-.128.106-.253c.23-.531.16-1.162-.25-1.572a1.27 1.27 0 0 1 0-1.794l1.035-1.034a1.27 1.27 0 0 1 1.794 0c.41.41 1.04.48 1.572.249a8 8 0 0 1 .253-.106c.539-.212.935-.708.935-1.286C10 2.568 10.568 2 11.269 2zM12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</nav>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
+539
@@ -0,0 +1,539 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`<SpaceSettingsVisibilityTab /> for a public space Access renders guest access section toggle 1`] = `
|
||||
<input
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
id="_r_h_"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`<SpaceSettingsVisibilityTab /> for a public space renders addresses section with publish toggle 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_SettingsTab"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsTab_sections"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSection"
|
||||
>
|
||||
<h2
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
Visibility
|
||||
</h2>
|
||||
<div
|
||||
class="mx_SettingsSection_subSections"
|
||||
>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
data-testid="access-fieldset"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
>
|
||||
Access
|
||||
</legend>
|
||||
<div
|
||||
class="mx_SettingsFieldset_description"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
Decide who can view and join mock-space.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFieldset_content"
|
||||
>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled"
|
||||
>
|
||||
<input
|
||||
aria-describedby="joinRule-invite-description"
|
||||
disabled=""
|
||||
id="joinRule-invite"
|
||||
name="joinRule"
|
||||
type="radio"
|
||||
value="invite"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
>
|
||||
Private (invite only)
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
<span
|
||||
id="joinRule-invite-description"
|
||||
>
|
||||
Only invited people can join.
|
||||
</span>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled mx_StyledRadioButton_checked"
|
||||
>
|
||||
<input
|
||||
aria-describedby="joinRule-public-description"
|
||||
checked=""
|
||||
disabled=""
|
||||
id="joinRule-public"
|
||||
name="joinRule"
|
||||
type="radio"
|
||||
value="public"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
>
|
||||
Public
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
<span
|
||||
id="joinRule-public-description"
|
||||
>
|
||||
Anyone can find and join.
|
||||
</span>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
class="mx_AccessibleButton mx_SettingsTab_showAdvanced mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link"
|
||||
data-testid="toggle-guest-access-btn"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Show advanced
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_udcm8_10"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
id="_r_3j_"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_udcm8_34"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="_r_3j_"
|
||||
>
|
||||
Preview Space
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id="radix-_r_3l_"
|
||||
>
|
||||
Allow people to preview your space before they join.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<strong>
|
||||
Recommended for public spaces.
|
||||
</strong>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</fieldset>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSection"
|
||||
>
|
||||
<h2
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
Address
|
||||
</h2>
|
||||
<div
|
||||
class="mx_SettingsSection_subSections"
|
||||
>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
data-testid="published-address-fieldset"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
>
|
||||
Published Addresses
|
||||
</legend>
|
||||
<div
|
||||
class="mx_SettingsFieldset_description"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
Published addresses can be used by anyone on any server to join your space. To publish an address, it needs to be set as a local address first.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFieldset_content"
|
||||
>
|
||||
<div
|
||||
class="mx_Field mx_Field_select"
|
||||
>
|
||||
<select
|
||||
disabled=""
|
||||
id="canonicalAlias"
|
||||
label="Main address"
|
||||
placeholder="Main address"
|
||||
type="text"
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
>
|
||||
not specified
|
||||
</option>
|
||||
</select>
|
||||
<label
|
||||
for="canonicalAlias"
|
||||
>
|
||||
Main address
|
||||
</label>
|
||||
<svg
|
||||
class="mx_Field_select_chevron"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 14.95q-.2 0-.375-.062a.9.9 0 0 1-.325-.213l-4.6-4.6a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l3.9 3.9 3.9-3.9a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7l-4.6 4.6q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_udcm8_10"
|
||||
>
|
||||
<input
|
||||
class="_input_udcm8_24"
|
||||
disabled=""
|
||||
id="_r_3u_"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_udcm8_34"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="_r_3u_"
|
||||
>
|
||||
Publish this room to the public in matrix.org's room directory?
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id="radix-_r_40_"
|
||||
>
|
||||
You must have permission to set the main address to publish this room.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<datalist
|
||||
id="mx_AliasSettings_altRecommendations"
|
||||
/>
|
||||
<div
|
||||
class="mx_EditableItemList"
|
||||
id="roomAltAliases"
|
||||
>
|
||||
<div
|
||||
class="mx_EditableItemList_label"
|
||||
>
|
||||
No other published addresses yet, add one below
|
||||
</div>
|
||||
<ul />
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
data-testid="local-address-fieldset"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
>
|
||||
Local Addresses
|
||||
</legend>
|
||||
<div
|
||||
class="mx_SettingsFieldset_description"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
Set addresses for this space so users can find this space through your homeserver (matrix.org)
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFieldset_content"
|
||||
>
|
||||
<details>
|
||||
<summary
|
||||
class="mx_AliasSettings_localAddresses"
|
||||
>
|
||||
Show more
|
||||
</summary>
|
||||
<div
|
||||
class="mx_EditableItemList"
|
||||
id="roomAliases"
|
||||
>
|
||||
<div
|
||||
class="mx_EditableItemList_label"
|
||||
>
|
||||
This space has no local addresses
|
||||
</div>
|
||||
<div
|
||||
class="mx_Field mx_Field_input mx_RoomAliasField mx_Field_labelAlwaysTopLeft"
|
||||
>
|
||||
<span
|
||||
class="mx_Field_prefix"
|
||||
>
|
||||
<span>
|
||||
#
|
||||
</span>
|
||||
</span>
|
||||
<input
|
||||
autocomplete="off"
|
||||
id="mx_Field_9"
|
||||
label="Room address"
|
||||
maxlength="243"
|
||||
placeholder="e.g. my-room"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_Field_9"
|
||||
>
|
||||
Room address
|
||||
</label>
|
||||
<span
|
||||
class="mx_Field_postfix"
|
||||
>
|
||||
<span
|
||||
title=":matrix.org"
|
||||
>
|
||||
:matrix.org
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Add
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<SpaceSettingsVisibilityTab /> renders container 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_SettingsTab"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsTab_sections"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSection"
|
||||
>
|
||||
<h2
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
Visibility
|
||||
</h2>
|
||||
<div
|
||||
class="mx_SettingsSection_subSections"
|
||||
>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
data-testid="access-fieldset"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
>
|
||||
Access
|
||||
</legend>
|
||||
<div
|
||||
class="mx_SettingsFieldset_description"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsSubsection_text"
|
||||
>
|
||||
Decide who can view and join mock-space.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFieldset_content"
|
||||
>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled mx_StyledRadioButton_checked"
|
||||
>
|
||||
<input
|
||||
aria-describedby="joinRule-invite-description"
|
||||
checked=""
|
||||
disabled=""
|
||||
id="joinRule-invite"
|
||||
name="joinRule"
|
||||
type="radio"
|
||||
value="invite"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
>
|
||||
Private (invite only)
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
<span
|
||||
id="joinRule-invite-description"
|
||||
>
|
||||
Only invited people can join.
|
||||
</span>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled"
|
||||
>
|
||||
<input
|
||||
aria-describedby="joinRule-public-description"
|
||||
disabled=""
|
||||
id="joinRule-public"
|
||||
name="joinRule"
|
||||
type="radio"
|
||||
value="public"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
>
|
||||
Public
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
<span
|
||||
id="joinRule-public-description"
|
||||
>
|
||||
Anyone can find and join.
|
||||
</span>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<div
|
||||
class="_inline-field_19upo_32"
|
||||
>
|
||||
<div
|
||||
class="_inline-field-control_19upo_44"
|
||||
>
|
||||
<div
|
||||
class="_container_udcm8_10"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_udcm8_24"
|
||||
id="_r_0_"
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_udcm8_34"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_inline-field-body_19upo_38"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="_r_0_"
|
||||
>
|
||||
Preview Space
|
||||
</label>
|
||||
<span
|
||||
class="_message_19upo_85 _help-message_19upo_91"
|
||||
id="radix-_r_2_"
|
||||
>
|
||||
Allow people to preview your space before they join.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<strong>
|
||||
Recommended for public spaces.
|
||||
</strong>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</fieldset>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`SpaceButton metaspace should render notificationState if one is provided 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
aria-label="People"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_active"
|
||||
data-testid="create-space-button"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarPlaceholder"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_icon"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SpacePanel_badgeContainer"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_level_notification mx_NotificationBadge_2char cpd-theme-light"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<span
|
||||
class="mx_NotificationBadge_count"
|
||||
>
|
||||
8
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="mx_SpaceButton_name"
|
||||
>
|
||||
People
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`SpaceItem should render a space with subspaces 1`] = `
|
||||
<DocumentFragment>
|
||||
<li
|
||||
aria-expanded="false"
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem collapsed hasSubSpaces"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Root Space"
|
||||
class="mx_AccessibleButton mx_SpaceButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
aria-label="Expand"
|
||||
class="mx_AccessibleButton mx_SpaceButton_toggleCollapse"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<span
|
||||
aria-label="Avatar"
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="_image_zysgz_43"
|
||||
data-type="round"
|
||||
height="32px"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
src="http://this.is.a.url/avatar.url/room.png"
|
||||
width="32px"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_SpaceButton_name"
|
||||
>
|
||||
Root Space
|
||||
</span>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Space options"
|
||||
class="mx_AccessibleButton mx_SpaceButton_menuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`SpaceItem should render a space with subspaces 2`] = `
|
||||
<DocumentFragment>
|
||||
<li
|
||||
aria-expanded="true"
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem hasSubSpaces"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Root Space"
|
||||
class="mx_AccessibleButton mx_SpaceButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
aria-label="Collapse"
|
||||
class="mx_AccessibleButton mx_SpaceButton_toggleCollapse"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 14.95q-.2 0-.375-.062a.9.9 0 0 1-.325-.213l-4.6-4.6a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l3.9 3.9 3.9-3.9a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7l-4.6 4.6q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<span
|
||||
aria-label="Avatar"
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="_image_zysgz_43"
|
||||
data-type="round"
|
||||
height="32px"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
src="http://this.is.a.url/avatar.url/room.png"
|
||||
width="32px"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_SpaceButton_name"
|
||||
>
|
||||
Root Space
|
||||
</span>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Space options"
|
||||
class="mx_AccessibleButton mx_SpaceButton_menuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
class="mx_SpaceTreeLevel"
|
||||
role="group"
|
||||
>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Subspace"
|
||||
class="mx_AccessibleButton mx_SpaceButton"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<span
|
||||
aria-label="Avatar"
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar"
|
||||
data-color="4"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
style="--cpd-avatar-size: 24px;"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="_image_zysgz_43"
|
||||
data-type="round"
|
||||
height="24px"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
src="http://this.is.a.url/avatar.url/room.png"
|
||||
width="24px"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_SpaceButton_name"
|
||||
>
|
||||
Subspace
|
||||
</span>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Space options"
|
||||
class="mx_AccessibleButton mx_SpaceButton_menuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
+333
@@ -0,0 +1,333 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`ThreadsActivityCentre renders notifications matching the snapshot 1`] = `
|
||||
<div
|
||||
aria-labelledby="radix-_r_1m_"
|
||||
aria-orientation="vertical"
|
||||
class="_menu_1w1u7_8"
|
||||
data-align="start"
|
||||
data-orientation="vertical"
|
||||
data-radix-menu-content=""
|
||||
data-side="top"
|
||||
data-state="open"
|
||||
dir="ltr"
|
||||
id="radix-_r_1n_"
|
||||
role="menu"
|
||||
style="outline: none; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); pointer-events: auto;"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
class="_typography_6v6n8_153 _font-body-sm-semibold_6v6n8_36 _menu-title_1sgvx_8 _title_1w1u7_63"
|
||||
id="_r_1u_"
|
||||
>
|
||||
Threads activity
|
||||
</h3>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_rows"
|
||||
>
|
||||
<button
|
||||
class="mx_ThreadsActivityCentreRow _item_lqfwq_8 _interactive_lqfwq_26"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DecoratedRoomAvatar _icon_lqfwq_50"
|
||||
>
|
||||
<span
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar _avatar-imageless_zysgz_55"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_lqfwq_34"
|
||||
>
|
||||
This is a real highlight
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_lqfwq_59"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_level_highlight mx_NotificationBadge_dot"
|
||||
>
|
||||
<span
|
||||
class="mx_NotificationBadge_count"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
class="mx_ThreadsActivityCentreRow _item_lqfwq_8 _interactive_lqfwq_26"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DecoratedRoomAvatar _icon_lqfwq_50"
|
||||
>
|
||||
<span
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar _avatar-imageless_zysgz_55"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
A
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_lqfwq_34"
|
||||
>
|
||||
A notification
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_lqfwq_59"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_level_notification mx_NotificationBadge_dot"
|
||||
>
|
||||
<span
|
||||
class="mx_NotificationBadge_count"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ThreadsActivityCentre should match snapshot when empty 1`] = `
|
||||
<div
|
||||
aria-labelledby="radix-_r_2a_"
|
||||
aria-orientation="vertical"
|
||||
class="_menu_1w1u7_8"
|
||||
data-align="start"
|
||||
data-orientation="vertical"
|
||||
data-radix-menu-content=""
|
||||
data-side="top"
|
||||
data-state="open"
|
||||
dir="ltr"
|
||||
id="radix-_r_2b_"
|
||||
role="menu"
|
||||
style="outline: none; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); pointer-events: auto;"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
class="_typography_6v6n8_153 _font-body-sm-semibold_6v6n8_36 _menu-title_1sgvx_8 _title_1w1u7_63"
|
||||
id="_r_2i_"
|
||||
>
|
||||
Threads activity
|
||||
</h3>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_rows"
|
||||
>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_emptyCaption"
|
||||
>
|
||||
You don't have rooms with thread notifications yet.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ThreadsActivityCentre should order the room with the same notification level by most recent 1`] = `
|
||||
<div
|
||||
aria-labelledby="radix-_r_2j_"
|
||||
aria-orientation="vertical"
|
||||
class="_menu_1w1u7_8"
|
||||
data-align="start"
|
||||
data-orientation="vertical"
|
||||
data-radix-menu-content=""
|
||||
data-side="top"
|
||||
data-state="open"
|
||||
dir="ltr"
|
||||
id="radix-_r_2k_"
|
||||
role="menu"
|
||||
style="outline: none; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); pointer-events: auto;"
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
class="_typography_6v6n8_153 _font-body-sm-semibold_6v6n8_36 _menu-title_1sgvx_8 _title_1w1u7_63"
|
||||
id="_r_2r_"
|
||||
>
|
||||
Threads activity
|
||||
</h3>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_rows"
|
||||
>
|
||||
<button
|
||||
class="mx_ThreadsActivityCentreRow _item_lqfwq_8 _interactive_lqfwq_26"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DecoratedRoomAvatar _icon_lqfwq_50"
|
||||
>
|
||||
<span
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar _avatar-imageless_zysgz_55"
|
||||
data-color="5"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_lqfwq_34"
|
||||
>
|
||||
This is a third real highlight
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_lqfwq_59"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_level_highlight mx_NotificationBadge_dot"
|
||||
>
|
||||
<span
|
||||
class="mx_NotificationBadge_count"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
class="mx_ThreadsActivityCentreRow _item_lqfwq_8 _interactive_lqfwq_26"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DecoratedRoomAvatar _icon_lqfwq_50"
|
||||
>
|
||||
<span
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar _avatar-imageless_zysgz_55"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_lqfwq_34"
|
||||
>
|
||||
This is a real highlight
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_lqfwq_59"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_level_highlight mx_NotificationBadge_dot"
|
||||
>
|
||||
<span
|
||||
class="mx_NotificationBadge_count"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
class="mx_ThreadsActivityCentreRow _item_lqfwq_8 _interactive_lqfwq_26"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_DecoratedRoomAvatar _icon_lqfwq_50"
|
||||
>
|
||||
<span
|
||||
class="_avatar_zysgz_8 mx_BaseAvatar _avatar-imageless_zysgz_55"
|
||||
data-color="4"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 _label_lqfwq_34"
|
||||
>
|
||||
This is a second real highlight
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_lqfwq_59"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="8 0 8 24"
|
||||
width="8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.7 17.3a.95.95 0 0 1-.275-.7q0-.425.275-.7l3.9-3.9-3.9-3.9a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l4.6 4.6q.15.15.213.325.062.175.062.375t-.062.375a.9.9 0 0 1-.213.325l-4.6 4.6a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_level_highlight mx_NotificationBadge_dot"
|
||||
>
|
||||
<span
|
||||
class="mx_NotificationBadge_count"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
* Copyright 2024 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 {
|
||||
type MatrixClient,
|
||||
MatrixEventEvent,
|
||||
NotificationCountType,
|
||||
PendingEventOrdering,
|
||||
Room,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { renderHook, act } from "jest-matrix-react";
|
||||
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import { populateThread } from "../../../../test-utils/threads";
|
||||
import { NotificationLevel } from "../../../../../src/stores/notifications/NotificationLevel";
|
||||
import { useUnreadThreadRooms } from "../../../../../src/components/views/spaces/threads-activity-centre/useUnreadThreadRooms";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
|
||||
describe("useUnreadThreadRooms", () => {
|
||||
let client: MatrixClient;
|
||||
let room: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
client = stubClient();
|
||||
client.supportsThreads = () => true;
|
||||
room = new Room("!room1:example.org", client, "@fee:bar", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("has no notifications with no rooms", async () => {
|
||||
const { result } = renderHook(() => useUnreadThreadRooms(false));
|
||||
const { greatestNotificationLevel, rooms } = result.current;
|
||||
|
||||
expect(greatestNotificationLevel).toBe(NotificationLevel.None);
|
||||
expect(rooms.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("an activity notification is ignored by default", async () => {
|
||||
const notifThreadInfo = await populateThread({
|
||||
room: room,
|
||||
client: client,
|
||||
authorId: "@foo:bar",
|
||||
participantUserIds: ["@fee:bar"],
|
||||
});
|
||||
room.setThreadUnreadNotificationCount(notifThreadInfo.thread.id, NotificationCountType.Total, 0);
|
||||
|
||||
client.getVisibleRooms = jest.fn().mockReturnValue([room]);
|
||||
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MatrixClientContext.Provider value={client}>{children}</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useUnreadThreadRooms(true), { wrapper });
|
||||
const { greatestNotificationLevel, rooms } = result.current;
|
||||
|
||||
expect(greatestNotificationLevel).toBe(NotificationLevel.None);
|
||||
expect(rooms.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("an activity notification is displayed with the setting enabled", async () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
|
||||
const notifThreadInfo = await populateThread({
|
||||
room: room,
|
||||
client: client,
|
||||
authorId: "@foo:bar",
|
||||
participantUserIds: ["@fee:bar"],
|
||||
});
|
||||
room.setThreadUnreadNotificationCount(notifThreadInfo.thread.id, NotificationCountType.Total, 0);
|
||||
|
||||
client.getVisibleRooms = jest.fn().mockReturnValue([room]);
|
||||
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MatrixClientContext.Provider value={client}>{children}</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useUnreadThreadRooms(true), { wrapper });
|
||||
const { greatestNotificationLevel, rooms } = result.current;
|
||||
|
||||
expect(greatestNotificationLevel).toBe(NotificationLevel.Activity);
|
||||
expect(rooms.length).toEqual(1);
|
||||
});
|
||||
|
||||
it("a notification and a highlight summarise to a highlight", async () => {
|
||||
const notifThreadInfo = await populateThread({
|
||||
room: room,
|
||||
client: client,
|
||||
authorId: "@foo:bar",
|
||||
participantUserIds: ["@fee:bar"],
|
||||
});
|
||||
room.setThreadUnreadNotificationCount(notifThreadInfo.thread.id, NotificationCountType.Total, 1);
|
||||
|
||||
const highlightThreadInfo = await populateThread({
|
||||
room: room,
|
||||
client: client,
|
||||
authorId: "@foo:bar",
|
||||
participantUserIds: ["@fee:bar"],
|
||||
});
|
||||
room.setThreadUnreadNotificationCount(highlightThreadInfo.thread.id, NotificationCountType.Highlight, 1);
|
||||
|
||||
client.getVisibleRooms = jest.fn().mockReturnValue([room]);
|
||||
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MatrixClientContext.Provider value={client}>{children}</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useUnreadThreadRooms(true), { wrapper });
|
||||
const { greatestNotificationLevel, rooms } = result.current;
|
||||
|
||||
expect(greatestNotificationLevel).toBe(NotificationLevel.Highlight);
|
||||
expect(rooms.length).toEqual(1);
|
||||
});
|
||||
|
||||
describe("updates", () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it("updates on decryption within 1s", async () => {
|
||||
const notifThreadInfo = await populateThread({
|
||||
room: room,
|
||||
client: client,
|
||||
authorId: "@foo:bar",
|
||||
participantUserIds: ["@fee:bar"],
|
||||
});
|
||||
room.setThreadUnreadNotificationCount(notifThreadInfo.thread.id, NotificationCountType.Total, 0);
|
||||
|
||||
client.getVisibleRooms = jest.fn().mockReturnValue([room]);
|
||||
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MatrixClientContext.Provider value={client}>{children}</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
const { result } = renderHook(() => useUnreadThreadRooms(true), { wrapper });
|
||||
|
||||
expect(result.current.greatestNotificationLevel).toBe(NotificationLevel.None);
|
||||
|
||||
act(() => {
|
||||
room.setThreadUnreadNotificationCount(notifThreadInfo.thread.id, NotificationCountType.Highlight, 1);
|
||||
client.emit(MatrixEventEvent.Decrypted, notifThreadInfo.thread.events[0]);
|
||||
|
||||
jest.advanceTimersByTime(1000);
|
||||
});
|
||||
|
||||
expect(result.current.greatestNotificationLevel).toBe(NotificationLevel.Highlight);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user