Merge matrix-react-sdk into element-web
Merge remote-tracking branch 'repomerge/t3chguy/repomerge' into t3chguy/repo-merge 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
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { render } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { 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
|
||||
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.findByText("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
|
||||
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,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
|
||||
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 { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { MetaSpace, 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 { 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-var-requires
|
||||
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,239 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||
import { act, fireEvent, render, RenderResult } from "jest-matrix-react";
|
||||
import { EventType, MatrixClient, Room, GuestAccess, HistoryVisibility, JoinRule } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import _SpaceSettingsVisibilityTab from "../../../../../src/components/views/spaces/SpaceSettingsVisibilityTab";
|
||||
import {
|
||||
createTestClient,
|
||||
mkEvent,
|
||||
wrapInMatrixClientContext,
|
||||
mkSpace,
|
||||
mockStateEventImplementation,
|
||||
} from "../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
|
||||
const SpaceSettingsVisibilityTab = wrapInMatrixClientContext(_SpaceSettingsVisibilityTab);
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
const mockMatrixClient = createTestClient() as MatrixClient;
|
||||
|
||||
const makeJoinEvent = (rule: JoinRule = JoinRule.Invite) =>
|
||||
mkEvent({
|
||||
type: EventType.RoomJoinRules,
|
||||
event: true,
|
||||
content: {
|
||||
join_rule: rule,
|
||||
},
|
||||
} as any);
|
||||
const makeGuestAccessEvent = (rule: GuestAccess = GuestAccess.CanJoin) =>
|
||||
mkEvent({
|
||||
type: EventType.RoomGuestAccess,
|
||||
event: true,
|
||||
content: {
|
||||
guest_access: rule,
|
||||
},
|
||||
} as any);
|
||||
const makeHistoryEvent = (rule: HistoryVisibility = HistoryVisibility.Shared) =>
|
||||
mkEvent({
|
||||
type: EventType.RoomHistoryVisibility,
|
||||
event: true,
|
||||
content: {
|
||||
history_visibility: rule,
|
||||
},
|
||||
} as any);
|
||||
|
||||
const mockSpaceId = "mock-space";
|
||||
|
||||
// TODO case for canonical
|
||||
const makeMockSpace = (
|
||||
client: MatrixClient,
|
||||
joinRule: JoinRule = JoinRule.Invite,
|
||||
guestRule: GuestAccess = GuestAccess.CanJoin,
|
||||
historyRule: HistoryVisibility = HistoryVisibility.WorldReadable,
|
||||
): Room => {
|
||||
const events = [makeJoinEvent(joinRule), makeGuestAccessEvent(guestRule), makeHistoryEvent(historyRule)];
|
||||
const space = mkSpace(client, mockSpaceId);
|
||||
const getStateEvents = mockStateEventImplementation(events);
|
||||
mocked(space.currentState).getStateEvents.mockImplementation(getStateEvents);
|
||||
mocked(space.currentState).mayClientSendStateEvent.mockReturnValue(false);
|
||||
space.getJoinRule.mockReturnValue(joinRule);
|
||||
mocked(space.currentState).getJoinRule.mockReturnValue(joinRule);
|
||||
return space as unknown as Room;
|
||||
};
|
||||
const defaultProps = {
|
||||
matrixClient: mockMatrixClient,
|
||||
space: makeMockSpace(mockMatrixClient),
|
||||
closeSettingsFn: jest.fn(),
|
||||
};
|
||||
|
||||
const getComponent = (props = {}) => {
|
||||
return render(<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(randomString).mockImplementation(() => {
|
||||
return "testid_" + i++;
|
||||
});
|
||||
|
||||
(mockMatrixClient.sendStateEvent as jest.Mock).mockClear().mockResolvedValue({});
|
||||
MatrixClientPeg.get = jest.fn().mockReturnValue(mockMatrixClient);
|
||||
MatrixClientPeg.safeGet = jest.fn().mockReturnValue(mockMatrixClient);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
it("renders container", () => {
|
||||
const { asFragment } = getComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("for a private space", () => {
|
||||
const joinRule = JoinRule.Invite;
|
||||
it("does not render addresses section", () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule);
|
||||
const { queryByTestId } = getComponent({ space });
|
||||
|
||||
expect(queryByTestId("published-address-fieldset")).toBeFalsy();
|
||||
expect(queryByTestId("local-address-fieldset")).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("for a public space", () => {
|
||||
const joinRule = JoinRule.Public;
|
||||
const guestRule = GuestAccess.CanJoin;
|
||||
const historyRule = HistoryVisibility.Joined;
|
||||
|
||||
describe("Access", () => {
|
||||
it("renders guest access section toggle", async () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
|
||||
const component = getComponent({ space });
|
||||
|
||||
await toggleGuestAccessSection(component);
|
||||
|
||||
expect(getGuestAccessToggle(component)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("send guest access event on toggle", async () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
|
||||
|
||||
const component = getComponent({ space });
|
||||
await toggleGuestAccessSection(component);
|
||||
const guestAccessInput = getGuestAccessToggle(component);
|
||||
|
||||
expect(guestAccessInput?.getAttribute("aria-checked")).toEqual("true");
|
||||
|
||||
fireEvent.click(guestAccessInput!);
|
||||
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||
mockSpaceId,
|
||||
EventType.RoomGuestAccess,
|
||||
// toggled off
|
||||
{ guest_access: GuestAccess.Forbidden },
|
||||
"",
|
||||
);
|
||||
|
||||
// toggled off
|
||||
expect(guestAccessInput?.getAttribute("aria-checked")).toEqual("false");
|
||||
});
|
||||
|
||||
it("renders error message when update fails", async () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
|
||||
(mockMatrixClient.sendStateEvent as jest.Mock).mockRejectedValue({});
|
||||
const component = getComponent({ space });
|
||||
await toggleGuestAccessSection(component);
|
||||
await act(() => {
|
||||
fireEvent.click(getGuestAccessToggle(component)!);
|
||||
});
|
||||
|
||||
expect(getErrorMessage(component)).toEqual("Failed to update the guest access of this space");
|
||||
});
|
||||
|
||||
it("disables guest access toggle when setting guest access is not allowed", async () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
|
||||
(space.currentState.maySendStateEvent as jest.Mock).mockReturnValue(false);
|
||||
const component = getComponent({ space });
|
||||
|
||||
await toggleGuestAccessSection(component);
|
||||
|
||||
expect(getGuestAccessToggle(component)?.getAttribute("aria-disabled")).toEqual("true");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Preview", () => {
|
||||
it("renders preview space toggle", () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
|
||||
const component = getComponent({ space });
|
||||
|
||||
// toggle off because space settings is != WorldReadable
|
||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false");
|
||||
});
|
||||
|
||||
it("updates history visibility on toggle", () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
|
||||
const component = getComponent({ space });
|
||||
|
||||
// toggle off because space settings is != WorldReadable
|
||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false");
|
||||
|
||||
fireEvent.click(getHistoryVisibilityToggle(component)!);
|
||||
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||
mockSpaceId,
|
||||
EventType.RoomHistoryVisibility,
|
||||
{ history_visibility: HistoryVisibility.WorldReadable },
|
||||
"",
|
||||
);
|
||||
|
||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("true");
|
||||
});
|
||||
|
||||
it("renders error message when history update fails", async () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
|
||||
(mockMatrixClient.sendStateEvent as jest.Mock).mockRejectedValue({});
|
||||
const component = getComponent({ space });
|
||||
|
||||
await act(() => {
|
||||
fireEvent.click(getHistoryVisibilityToggle(component)!);
|
||||
});
|
||||
|
||||
expect(getErrorMessage(component)).toEqual("Failed to update the history visibility of this space");
|
||||
});
|
||||
|
||||
it("disables room preview toggle when history visibility changes are not allowed", () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
|
||||
(space.currentState.maySendStateEvent as jest.Mock).mockReturnValue(false);
|
||||
const component = getComponent({ space });
|
||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-disabled")).toEqual("true");
|
||||
});
|
||||
});
|
||||
|
||||
it("renders addresses section", () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
|
||||
const { getByTestId } = getComponent({ space });
|
||||
|
||||
expect(getByTestId("published-address-fieldset")).toBeTruthy();
|
||||
expect(getByTestId("local-address-fieldset")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, getByTestId, render } from "jest-matrix-react";
|
||||
|
||||
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 { SpaceButton } from "../../../../../src/components/views/spaces/SpaceTreeLevel";
|
||||
import { MetaSpace, 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-var-requires
|
||||
const EventEmitter = require("events");
|
||||
class MockSpaceStore extends EventEmitter {
|
||||
activeSpace: SpaceKey = "!space1";
|
||||
setActiveSpace = 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { 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";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
|
||||
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,
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await SettingsStore.setValue("feature_release_announcement", null, SettingLevel.DEVICE, false);
|
||||
});
|
||||
|
||||
it("should render the threads activity centre button", async () => {
|
||||
renderTAC();
|
||||
expect(getTACButton()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the release announcement", async () => {
|
||||
// Enable release announcement
|
||||
await SettingsStore.setValue("feature_release_announcement", null, SettingLevel.DEVICE, true);
|
||||
|
||||
renderTAC();
|
||||
expect(document.body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render not display the tooltip when the release announcement is displayed", async () => {
|
||||
// Enable release announcement
|
||||
await SettingsStore.setValue("feature_release_announcement", null, SettingLevel.DEVICE, true);
|
||||
|
||||
renderTAC();
|
||||
|
||||
// The tooltip should not be displayed
|
||||
await userEvent.hover(getTACButton());
|
||||
expect(screen.queryByRole("tooltip")).toBeNull();
|
||||
});
|
||||
|
||||
it("should close the release announcement when the TAC button is clicked", async () => {
|
||||
// Enable release announcement
|
||||
await SettingsStore.setValue("feature_release_announcement", null, SettingLevel.DEVICE, true);
|
||||
|
||||
renderTAC();
|
||||
await userEvent.click(getTACButton());
|
||||
expect(getTACMenu()).toBeInTheDocument();
|
||||
expect(document.body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
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"
|
||||
>
|
||||
<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_mcap2_17 mx_BaseAvatar"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="square"
|
||||
style="--cpd-avatar-size: 40px;"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="_image_mcap2_50"
|
||||
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"
|
||||
/>
|
||||
</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"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`QuickSettingsButton should render the quick settings button in expanded mode 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
aria-expanded="true"
|
||||
aria-label="Quick settings"
|
||||
class="mx_AccessibleButton mx_QuickSettingsButton expanded"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Settings
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -0,0 +1,289 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SpacePanel /> should show all activated MetaSpaces in the correct order 1`] = `
|
||||
<DocumentFragment>
|
||||
<nav
|
||||
aria-label="Spaces"
|
||||
class="mx_SpacePanel collapsed"
|
||||
>
|
||||
<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_mcap2_17 mx_BaseAvatar mx_UserMenu_userAvatar_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
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"
|
||||
/>
|
||||
</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_home mx_SpaceButton_narrow"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_selectionWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarWrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_avatarPlaceholder"
|
||||
>
|
||||
<div
|
||||
class="mx_SpaceButton_icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Options"
|
||||
class="mx_AccessibleButton mx_SpaceButton_menuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem collapsed"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Favourites"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_favourites mx_SpaceButton_narrow"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem collapsed"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="People"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_people mx_SpaceButton_narrow"
|
||||
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>
|
||||
</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_orphans mx_SpaceButton_narrow"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
aria-selected="false"
|
||||
class="mx_SpaceItem collapsed"
|
||||
role="treeitem"
|
||||
>
|
||||
<div
|
||||
aria-label="Conferences"
|
||||
class="mx_AccessibleButton mx_SpaceButton mx_SpaceButton_videoRooms mx_SpaceButton_narrow"
|
||||
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>
|
||||
</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"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_container"
|
||||
>
|
||||
<button
|
||||
aria-controls="floating-ui-40"
|
||||
aria-describedby="floating-ui-40"
|
||||
aria-expanded="true"
|
||||
aria-haspopup="dialog"
|
||||
aria-label="Threads"
|
||||
aria-labelledby="floating-ui-33"
|
||||
class="_icon-button_bh2qc_17 mx_ThreadsActivityCentreButton"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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-2Zm3 7h10a.97.97 0 0 0 .712-.287A.967.967 0 0 0 18 9a.967.967 0 0 0-.288-.713A.968.968 0 0 0 17 8H7a.968.968 0 0 0-.713.287A.968.968 0 0 0 6 9c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 4h6c.283 0 .52-.096.713-.287A.968.968 0 0 0 14 13a.968.968 0 0 0-.287-.713A.968.968 0 0 0 13 12H7a.967.967 0 0 0-.713.287A.968.968 0 0 0 6 13c0 .283.096.52.287.713.192.191.43.287.713.287Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<span
|
||||
data-floating-ui-focus-guard=""
|
||||
data-type="outside"
|
||||
role="button"
|
||||
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<span
|
||||
aria-owns="undefined"
|
||||
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
|
||||
/>
|
||||
<span
|
||||
data-floating-ui-focus-guard=""
|
||||
data-type="outside"
|
||||
role="button"
|
||||
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-label="Quick settings"
|
||||
class="mx_AccessibleButton mx_QuickSettingsButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</nav>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SpaceSettingsVisibilityTab /> for a public space Access renders guest access section toggle 1`] = `
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="false"
|
||||
aria-labelledby="mx_LabelledToggleSwitch_testid_1"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on mx_ToggleSwitch_enabled"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
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>
|
||||
<div
|
||||
class="mx_SettingsTab_toggleWithDescription"
|
||||
>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_label"
|
||||
>
|
||||
<div
|
||||
id="mx_LabelledToggleSwitch_testid_0"
|
||||
>
|
||||
Preview Space
|
||||
</div>
|
||||
</span>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="false"
|
||||
aria-labelledby="mx_LabelledToggleSwitch_testid_0"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on mx_ToggleSwitch_enabled"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
Allow people to preview your space before they join.
|
||||
<br />
|
||||
<strong>
|
||||
Recommended for public spaces.
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -0,0 +1,49 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
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>
|
||||
`;
|
||||
+651
@@ -0,0 +1,651 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ThreadsActivityCentre renders notifications matching the snapshot 1`] = `
|
||||
<div
|
||||
aria-labelledby="radix-16"
|
||||
aria-orientation="vertical"
|
||||
class="_menu_1x5h1_17"
|
||||
data-align="start"
|
||||
data-orientation="vertical"
|
||||
data-radix-menu-content=""
|
||||
data-side="top"
|
||||
data-state="open"
|
||||
dir="ltr"
|
||||
id="radix-17"
|
||||
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_yh5dq_162 _font-body-sm-semibold_yh5dq_45 _title_1x5h1_83"
|
||||
id=":r6:"
|
||||
>
|
||||
Threads activity
|
||||
</h3>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_rows"
|
||||
>
|
||||
<button
|
||||
class="mx_ThreadsActivityCentreRow _item_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="_icon_8j2l6_43"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
This is a real highlight
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_8j2l6_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.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7l3.9-3.9-3.9-3.9a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l4.6 4.6c.1.1.17.208.213.325.041.117.062.242.062.375s-.02.258-.063.375a.877.877 0 0 1-.212.325l-4.6 4.6a.948.948 0 0 1-.7.275.948.948 0 0 1-.7-.275Z"
|
||||
/>
|
||||
</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_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="_icon_8j2l6_43"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
A
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
A notification
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_8j2l6_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.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7l3.9-3.9-3.9-3.9a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l4.6 4.6c.1.1.17.208.213.325.041.117.062.242.062.375s-.02.258-.063.375a.877.877 0 0 1-.212.325l-4.6 4.6a.948.948 0 0 1-.7.275.948.948 0 0 1-.7-.275Z"
|
||||
/>
|
||||
</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 close the release announcement when the TAC button is clicked 1`] = `
|
||||
<body
|
||||
data-scroll-locked="1"
|
||||
style="pointer-events: none;"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
data-aria-hidden="true"
|
||||
data-radix-focus-guard=""
|
||||
style="outline: none; opacity: 0; position: fixed; pointer-events: none;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-aria-hidden="true"
|
||||
>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_container"
|
||||
>
|
||||
<button
|
||||
aria-controls="radix-3"
|
||||
aria-disabled="false"
|
||||
aria-expanded="true"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Threads"
|
||||
aria-labelledby="floating-ui-42"
|
||||
class="_icon-button_bh2qc_17 mx_ThreadsActivityCentreButton"
|
||||
data-state="open"
|
||||
id="radix-2"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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-2Zm3 7h10a.97.97 0 0 0 .712-.287A.967.967 0 0 0 18 9a.967.967 0 0 0-.288-.713A.968.968 0 0 0 17 8H7a.968.968 0 0 0-.713.287A.968.968 0 0 0 6 9c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 4h6c.283 0 .52-.096.713-.287A.968.968 0 0 0 14 13a.968.968 0 0 0-.287-.713A.968.968 0 0 0 13 12H7a.967.967 0 0 0-.713.287A.968.968 0 0 0 6 13c0 .283.096.52.287.713.192.191.43.287.713.287Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
data-aria-hidden="true"
|
||||
data-floating-ui-portal=""
|
||||
id="floating-ui-46"
|
||||
>
|
||||
<div
|
||||
class="_tooltip_1pslb_17 _invisible_1pslb_30"
|
||||
style="position: absolute; left: 0px; top: 0px; transform: translate(6px, 5px);"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_arrow_1pslb_42"
|
||||
height="10"
|
||||
style="position: absolute; pointer-events: none; right: calc(100% - 0px); transform: rotate(90deg); top: -1px;"
|
||||
viewBox="0 0 10 10"
|
||||
width="10"
|
||||
>
|
||||
<path
|
||||
d="M0,0 H10 L5,6 Q5,6 5,6 Z"
|
||||
stroke="none"
|
||||
/>
|
||||
<clippath
|
||||
id="floating-ui-47"
|
||||
>
|
||||
<rect
|
||||
height="10"
|
||||
width="10"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
</clippath>
|
||||
</svg>
|
||||
<span
|
||||
id="floating-ui-42"
|
||||
>
|
||||
Threads
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-radix-popper-content-wrapper=""
|
||||
dir="ltr"
|
||||
style="position: fixed; left: 0px; top: 0px; transform: translate(0px, -8px); min-width: max-content; --radix-popper-available-width: 0px; --radix-popper-available-height: -8px; --radix-popper-anchor-width: 0px; --radix-popper-anchor-height: 0px; --radix-popper-transform-origin: 0% 0px;"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="radix-2"
|
||||
aria-orientation="vertical"
|
||||
class="_menu_1x5h1_17"
|
||||
data-align="start"
|
||||
data-orientation="vertical"
|
||||
data-radix-menu-content=""
|
||||
data-side="top"
|
||||
data-state="open"
|
||||
dir="ltr"
|
||||
id="radix-3"
|
||||
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_yh5dq_162 _font-body-sm-semibold_yh5dq_45 _title_1x5h1_83"
|
||||
id=":r1:"
|
||||
>
|
||||
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>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
data-aria-hidden="true"
|
||||
data-radix-focus-guard=""
|
||||
style="outline: none; opacity: 0; position: fixed; pointer-events: none;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`ThreadsActivityCentre should match snapshot when empty 1`] = `
|
||||
<div
|
||||
aria-labelledby="radix-22"
|
||||
aria-orientation="vertical"
|
||||
class="_menu_1x5h1_17"
|
||||
data-align="start"
|
||||
data-orientation="vertical"
|
||||
data-radix-menu-content=""
|
||||
data-side="top"
|
||||
data-state="open"
|
||||
dir="ltr"
|
||||
id="radix-23"
|
||||
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_yh5dq_162 _font-body-sm-semibold_yh5dq_45 _title_1x5h1_83"
|
||||
id=":r8:"
|
||||
>
|
||||
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-24"
|
||||
aria-orientation="vertical"
|
||||
class="_menu_1x5h1_17"
|
||||
data-align="start"
|
||||
data-orientation="vertical"
|
||||
data-radix-menu-content=""
|
||||
data-side="top"
|
||||
data-state="open"
|
||||
dir="ltr"
|
||||
id="radix-25"
|
||||
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_yh5dq_162 _font-body-sm-semibold_yh5dq_45 _title_1x5h1_83"
|
||||
id=":r9:"
|
||||
>
|
||||
Threads activity
|
||||
</h3>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_rows"
|
||||
>
|
||||
<button
|
||||
class="mx_ThreadsActivityCentreRow _item_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="_icon_8j2l6_43"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="5"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
This is a third real highlight
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_8j2l6_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.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7l3.9-3.9-3.9-3.9a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l4.6 4.6c.1.1.17.208.213.325.041.117.062.242.062.375s-.02.258-.063.375a.877.877 0 0 1-.212.325l-4.6 4.6a.948.948 0 0 1-.7.275.948.948 0 0 1-.7-.275Z"
|
||||
/>
|
||||
</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_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="_icon_8j2l6_43"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
This is a real highlight
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_8j2l6_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.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7l3.9-3.9-3.9-3.9a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l4.6 4.6c.1.1.17.208.213.325.041.117.062.242.062.375s-.02.258-.063.375a.877.877 0 0 1-.212.325l-4.6 4.6a.948.948 0 0 1-.7.275.948.948 0 0 1-.7-.275Z"
|
||||
/>
|
||||
</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_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="primary"
|
||||
data-orientation="vertical"
|
||||
data-radix-collection-item=""
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="_icon_8j2l6_43"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="4"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
T
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
This is a second real highlight
|
||||
</span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_nav-hint_8j2l6_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.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7l3.9-3.9-3.9-3.9a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l4.6 4.6c.1.1.17.208.213.325.041.117.062.242.062.375s-.02.258-.063.375a.877.877 0 0 1-.212.325l-4.6 4.6a.948.948 0 0 1-.7.275.948.948 0 0 1-.7-.275Z"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="mx_NotificationBadge mx_NotificationBadge_visible mx_NotificationBadge_level_highlight mx_NotificationBadge_dot"
|
||||
>
|
||||
<span
|
||||
class="mx_NotificationBadge_count"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ThreadsActivityCentre should render the release announcement 1`] = `
|
||||
<body>
|
||||
<div
|
||||
data-floating-ui-inert=""
|
||||
>
|
||||
<div
|
||||
class="mx_ThreadsActivityCentre_container"
|
||||
>
|
||||
<button
|
||||
aria-controls="floating-ui-8"
|
||||
aria-describedby="floating-ui-8"
|
||||
aria-expanded="true"
|
||||
aria-haspopup="dialog"
|
||||
aria-label="Threads"
|
||||
aria-labelledby="floating-ui-10"
|
||||
class="_icon-button_bh2qc_17 mx_ThreadsActivityCentreButton"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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-2Zm3 7h10a.97.97 0 0 0 .712-.287A.967.967 0 0 0 18 9a.967.967 0 0 0-.288-.713A.968.968 0 0 0 17 8H7a.968.968 0 0 0-.713.287A.968.968 0 0 0 6 9c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 4h6c.283 0 .52-.096.713-.287A.968.968 0 0 0 14 13a.968.968 0 0 0-.287-.713A.968.968 0 0 0 13 12H7a.967.967 0 0 0-.713.287A.968.968 0 0 0 6 13c0 .283.096.52.287.713.192.191.43.287.713.287Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<span
|
||||
data-floating-ui-focus-guard=""
|
||||
data-type="outside"
|
||||
role="button"
|
||||
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<span
|
||||
aria-owns="floating-ui-15"
|
||||
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
|
||||
/>
|
||||
<span
|
||||
data-floating-ui-focus-guard=""
|
||||
data-type="outside"
|
||||
role="button"
|
||||
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-floating-ui-inert=""
|
||||
data-floating-ui-portal=""
|
||||
id="floating-ui-14"
|
||||
>
|
||||
<div
|
||||
class="_tooltip_1pslb_17 _invisible_1pslb_30"
|
||||
style="position: absolute; left: 0px; top: 0px; transform: translate(0px, 0px);"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_arrow_1pslb_42"
|
||||
height="10"
|
||||
style="position: absolute; pointer-events: none; right: calc(100% - 0px); transform: rotate(90deg);"
|
||||
viewBox="0 0 10 10"
|
||||
width="10"
|
||||
>
|
||||
<path
|
||||
d="M0,0 H10 L5,6 Q5,6 5,6 Z"
|
||||
stroke="none"
|
||||
/>
|
||||
<clippath
|
||||
id="floating-ui-16"
|
||||
>
|
||||
<rect
|
||||
height="10"
|
||||
width="10"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
</clippath>
|
||||
</svg>
|
||||
<span
|
||||
id="floating-ui-10"
|
||||
>
|
||||
Threads
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-floating-ui-portal=""
|
||||
id="floating-ui-15"
|
||||
>
|
||||
<span
|
||||
data-floating-ui-focus-guard=""
|
||||
data-floating-ui-inert=""
|
||||
data-type="inside"
|
||||
role="button"
|
||||
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="floating-ui-7"
|
||||
aria-labelledby="floating-ui-6"
|
||||
class="_content_1oa1y_17"
|
||||
id="floating-ui-8"
|
||||
role="dialog"
|
||||
style="position: absolute; left: 0px; top: 0px; transform: translate(0px, 0px);"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_arrow_1oa1y_62"
|
||||
height="20"
|
||||
style="position: absolute; pointer-events: none; right: calc(100% - 0px); transform: rotate(90deg);"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
>
|
||||
<path
|
||||
d="M0,0 H20 L10,12 Q10,12 10,12 Z"
|
||||
stroke="none"
|
||||
/>
|
||||
<clippath
|
||||
id="floating-ui-17"
|
||||
>
|
||||
<rect
|
||||
height="20"
|
||||
width="20"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
</clippath>
|
||||
</svg>
|
||||
<h3
|
||||
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83 _header_1oa1y_46"
|
||||
id="floating-ui-6"
|
||||
>
|
||||
Threads Activity Centre
|
||||
</h3>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-sm-regular_yh5dq_40 _description_1oa1y_52"
|
||||
id="floating-ui-7"
|
||||
>
|
||||
Threads notifications have moved, find them here from now on.
|
||||
</span>
|
||||
<button
|
||||
class="_button_i91xf_17 _button_1oa1y_57"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</div>
|
||||
<span
|
||||
data-floating-ui-focus-guard=""
|
||||
data-floating-ui-inert=""
|
||||
data-type="inside"
|
||||
role="button"
|
||||
style="border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: fixed; white-space: nowrap; width: 1px; top: 0px; left: 0px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { renderHook } from "@testing-library/react-hooks";
|
||||
import {
|
||||
MatrixClient,
|
||||
MatrixEventEvent,
|
||||
NotificationCountType,
|
||||
PendingEventOrdering,
|
||||
Room,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { 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