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:
commit
f0ee7f7905
3265 changed files with 484599 additions and 699 deletions
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
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, render, screen } from "jest-matrix-react";
|
||||
|
||||
import BaseCard from "../../../../../src/components/views/right_panel/BaseCard.tsx";
|
||||
import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelStore.ts";
|
||||
|
||||
jest.mock("../../../../../src/stores/right-panel/RightPanelStore", () => ({
|
||||
instance: {
|
||||
popCard: jest.fn(),
|
||||
roomPhaseHistory: [],
|
||||
},
|
||||
}));
|
||||
|
||||
describe("<BaseCard />", () => {
|
||||
it("should close when clicking X button", async () => {
|
||||
const { asFragment } = render(
|
||||
<BaseCard header="Heading text">
|
||||
<div>Content</div>
|
||||
</BaseCard>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole("heading")).toHaveTextContent("Heading text");
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(screen.getByTestId("base-card-close-button"));
|
||||
expect(RightPanelStore.instance.popCard).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
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 { mocked, Mocked } from "jest-mock";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixWidgetType } from "matrix-widget-api";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import ExtensionsCard from "../../../../../src/components/views/right_panel/ExtensionsCard";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import { IApp } from "../../../../../src/stores/WidgetStore";
|
||||
import WidgetUtils, { useWidgets } from "../../../../../src/utils/WidgetUtils";
|
||||
import { WidgetLayoutStore } from "../../../../../src/stores/widgets/WidgetLayoutStore";
|
||||
import { IntegrationManagers } from "../../../../../src/integrations/IntegrationManagers";
|
||||
|
||||
jest.mock("../../../../../src/utils/WidgetUtils");
|
||||
|
||||
describe("<ExtensionsCard />", () => {
|
||||
let client: Mocked<MatrixClient>;
|
||||
let room: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
client = mocked(stubClient());
|
||||
room = new Room("!room:server", client, client.getSafeUserId());
|
||||
mocked(WidgetUtils.getWidgetName).mockImplementation((app) => app?.name ?? "No Name");
|
||||
});
|
||||
|
||||
it("should render empty state", () => {
|
||||
mocked(useWidgets).mockReturnValue([]);
|
||||
const { asFragment } = render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
expect(screen.getByText("Boost productivity with more tools, widgets and bots")).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render widgets", async () => {
|
||||
mocked(useWidgets).mockReturnValue([
|
||||
{
|
||||
id: "id",
|
||||
roomId: room.roomId,
|
||||
eventId: "$event1",
|
||||
creatorUserId: client.getSafeUserId(),
|
||||
type: MatrixWidgetType.Custom,
|
||||
name: "Custom Widget",
|
||||
url: "http://url1",
|
||||
},
|
||||
{
|
||||
id: "jitsi",
|
||||
roomId: room.roomId,
|
||||
eventId: "$event2",
|
||||
creatorUserId: client.getSafeUserId(),
|
||||
type: MatrixWidgetType.JitsiMeet,
|
||||
name: "Jitsi",
|
||||
url: "http://jitsi",
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
const { asFragment } = render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
expect(screen.getByText("Custom Widget")).toBeInTheDocument();
|
||||
expect(screen.getByText("Jitsi")).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should show context menu on widget row", async () => {
|
||||
jest.spyOn(WidgetUtils, "canUserModifyWidgets").mockReturnValue(true);
|
||||
mocked(useWidgets).mockReturnValue([
|
||||
{
|
||||
id: "id",
|
||||
roomId: room.roomId,
|
||||
eventId: "$event1",
|
||||
creatorUserId: client.getSafeUserId(),
|
||||
type: MatrixWidgetType.Custom,
|
||||
name: "Custom Widget",
|
||||
url: "http://url1",
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
const { container } = render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
await userEvent.click(container.querySelector(".mx_ExtensionsCard_app_options")!);
|
||||
expect(document.querySelector(".mx_IconizedContextMenu")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should show set room layout button", async () => {
|
||||
jest.spyOn(WidgetLayoutStore.instance, "canCopyLayoutToRoom").mockReturnValue(true);
|
||||
mocked(useWidgets).mockReturnValue([
|
||||
{
|
||||
id: "id",
|
||||
roomId: room.roomId,
|
||||
eventId: "$event1",
|
||||
creatorUserId: client.getSafeUserId(),
|
||||
type: MatrixWidgetType.Custom,
|
||||
name: "Custom Widget",
|
||||
url: "http://url1",
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
expect(screen.getByText("Set layout for everyone")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show widget as pinned", async () => {
|
||||
jest.spyOn(WidgetLayoutStore.instance, "isInContainer").mockReturnValue(true);
|
||||
mocked(useWidgets).mockReturnValue([
|
||||
{
|
||||
id: "id",
|
||||
roomId: room.roomId,
|
||||
eventId: "$event1",
|
||||
creatorUserId: client.getSafeUserId(),
|
||||
type: MatrixWidgetType.Custom,
|
||||
name: "Custom Widget",
|
||||
url: "http://url1",
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
expect(screen.getByText("Custom Widget").closest(".mx_ExtensionsCard_Button_pinned")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should show cannot pin warning", async () => {
|
||||
jest.spyOn(WidgetLayoutStore.instance, "isInContainer").mockReturnValue(false);
|
||||
jest.spyOn(WidgetLayoutStore.instance, "canAddToContainer").mockReturnValue(false);
|
||||
mocked(useWidgets).mockReturnValue([
|
||||
{
|
||||
id: "id",
|
||||
roomId: room.roomId,
|
||||
eventId: "$event1",
|
||||
creatorUserId: client.getSafeUserId(),
|
||||
type: MatrixWidgetType.Custom,
|
||||
name: "Custom Widget",
|
||||
url: "http://url1",
|
||||
},
|
||||
] satisfies IApp[]);
|
||||
|
||||
render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
expect(screen.getByLabelText("You can only pin up to 3 widgets")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should should open integration manager on click", async () => {
|
||||
jest.spyOn(IntegrationManagers.sharedInstance(), "hasManager").mockReturnValue(false);
|
||||
const spy = jest.spyOn(IntegrationManagers.sharedInstance(), "openNoManagerDialog");
|
||||
render(<ExtensionsCard room={room} onClose={jest.fn()} />);
|
||||
await userEvent.click(screen.getByText("Add extensions"));
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
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, act, RenderResult, waitForElementToBeRemoved, screen, waitFor } from "jest-matrix-react";
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import {
|
||||
MatrixEvent,
|
||||
RoomStateEvent,
|
||||
Room,
|
||||
IMinimalEvent,
|
||||
EventType,
|
||||
RelationType,
|
||||
MsgType,
|
||||
M_POLL_KIND_DISCLOSED,
|
||||
EventTimeline,
|
||||
MatrixClient,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
|
||||
import { PollResponseEvent } from "matrix-js-sdk/src/extensible_events_v1/PollResponseEvent";
|
||||
import { PollEndEvent } from "matrix-js-sdk/src/extensible_events_v1/PollEndEvent";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import { stubClient, mkEvent, mkMessage, flushPromises } from "../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { PinnedMessagesCard } from "../../../../../src/components/views/right_panel/PinnedMessagesCard";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
import Modal from "../../../../../src/Modal";
|
||||
import { UnpinAllDialog } from "../../../../../src/components/views/dialogs/UnpinAllDialog";
|
||||
|
||||
describe("<PinnedMessagesCard />", () => {
|
||||
let cli: MockedObject<MatrixClient>;
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
cli = mocked(MatrixClientPeg.safeGet());
|
||||
cli.getUserId.mockReturnValue("@alice:example.org");
|
||||
cli.setRoomAccountData.mockResolvedValue({});
|
||||
cli.relations.mockResolvedValue({ originalEvent: {} as unknown as MatrixEvent, events: [] });
|
||||
});
|
||||
|
||||
const mkRoom = (localPins: MatrixEvent[], nonLocalPins: MatrixEvent[]): Room => {
|
||||
const room = new Room("!room:example.org", cli, "@me:example.org");
|
||||
// Deferred since we may be adding or removing pins later
|
||||
const pins = () => [...localPins, ...nonLocalPins];
|
||||
|
||||
// Insert pin IDs into room state
|
||||
jest.spyOn(room.getLiveTimeline().getState(EventTimeline.FORWARDS)!, "getStateEvents").mockImplementation(
|
||||
(): any =>
|
||||
mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomPinnedEvents,
|
||||
content: {
|
||||
pinned: pins().map((e) => e.getId()),
|
||||
},
|
||||
user: "@user:example.org",
|
||||
room: "!room:example.org",
|
||||
}),
|
||||
);
|
||||
|
||||
jest.spyOn(room.getLiveTimeline().getState(EventTimeline.FORWARDS)!, "mayClientSendStateEvent").mockReturnValue(
|
||||
true,
|
||||
);
|
||||
// poll end event validates against this
|
||||
jest.spyOn(
|
||||
room.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"maySendRedactionForEvent",
|
||||
).mockReturnValue(true);
|
||||
|
||||
// Return all pins over fetchRoomEvent
|
||||
cli.fetchRoomEvent.mockImplementation((roomId, eventId) => {
|
||||
const event = pins().find((e) => e.getId() === eventId)?.event;
|
||||
return Promise.resolve(event as IMinimalEvent);
|
||||
});
|
||||
|
||||
cli.getRoom.mockReturnValue(room);
|
||||
|
||||
return room;
|
||||
};
|
||||
|
||||
async function renderMessagePinList(room: Room): Promise<RenderResult> {
|
||||
const renderResult = render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<PinnedMessagesCard
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
// Wait a tick for state updates
|
||||
await act(() => sleep(0));
|
||||
|
||||
return renderResult;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param room
|
||||
*/
|
||||
async function emitPinUpdate(room: Room) {
|
||||
await act(async () => {
|
||||
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
||||
roomState.emit(
|
||||
RoomStateEvent.Events,
|
||||
new MatrixEvent({ type: EventType.RoomPinnedEvents }),
|
||||
roomState,
|
||||
null,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the pinned messages card with the given pinned messages.
|
||||
* Return the room, testing library helpers and functions to add and remove pinned messages.
|
||||
* @param localPins
|
||||
* @param nonLocalPins
|
||||
*/
|
||||
async function initPinnedMessagesCard(localPins: MatrixEvent[], nonLocalPins: MatrixEvent[]) {
|
||||
const room = mkRoom(localPins, nonLocalPins);
|
||||
const addLocalPinEvent = async (event: MatrixEvent) => {
|
||||
localPins.push(event);
|
||||
await emitPinUpdate(room);
|
||||
};
|
||||
const removeLastLocalPinEvent = async () => {
|
||||
localPins.pop();
|
||||
await emitPinUpdate(room);
|
||||
};
|
||||
const addNonLocalPinEvent = async (event: MatrixEvent) => {
|
||||
nonLocalPins.push(event);
|
||||
await emitPinUpdate(room);
|
||||
};
|
||||
const removeLastNonLocalPinEvent = async () => {
|
||||
nonLocalPins.pop();
|
||||
await emitPinUpdate(room);
|
||||
};
|
||||
const renderResult = await renderMessagePinList(room);
|
||||
|
||||
return {
|
||||
...renderResult,
|
||||
addLocalPinEvent,
|
||||
removeLastLocalPinEvent,
|
||||
addNonLocalPinEvent,
|
||||
removeLastNonLocalPinEvent,
|
||||
room,
|
||||
};
|
||||
}
|
||||
|
||||
const pin1 = mkMessage({
|
||||
event: true,
|
||||
room: "!room:example.org",
|
||||
user: "@alice:example.org",
|
||||
msg: "First pinned message",
|
||||
ts: 2,
|
||||
});
|
||||
const pin2 = mkMessage({
|
||||
event: true,
|
||||
room: "!room:example.org",
|
||||
user: "@alice:example.org",
|
||||
msg: "The second one",
|
||||
ts: 1,
|
||||
});
|
||||
|
||||
it("should show spinner whilst loading", async () => {
|
||||
const room = mkRoom([], [pin1]);
|
||||
render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<PinnedMessagesCard
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.queryAllByRole("progressbar"));
|
||||
});
|
||||
|
||||
it("should show the empty state when there are no pins", async () => {
|
||||
const { asFragment } = await initPinnedMessagesCard([], []);
|
||||
|
||||
expect(screen.getByText("Pin important messages so that they can be easily discovered")).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should show two pinned messages", async () => {
|
||||
const { asFragment } = await initPinnedMessagesCard([pin1], [pin2]);
|
||||
|
||||
await waitFor(() => expect(screen.queryAllByRole("listitem")).toHaveLength(2));
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should not show more than 100 messages", async () => {
|
||||
const events = Array.from({ length: 120 }, (_, i) =>
|
||||
mkMessage({
|
||||
event: true,
|
||||
room: "!room:example.org",
|
||||
user: "@alice:example.org",
|
||||
msg: `The message ${i}`,
|
||||
ts: i,
|
||||
}),
|
||||
);
|
||||
await initPinnedMessagesCard(events, []);
|
||||
|
||||
await waitFor(() => expect(screen.queryAllByRole("listitem")).toHaveLength(100));
|
||||
}, 15000);
|
||||
|
||||
it("should updates when messages are pinned", async () => {
|
||||
// Start with nothing pinned
|
||||
const { addLocalPinEvent, addNonLocalPinEvent } = await initPinnedMessagesCard([], []);
|
||||
|
||||
await waitFor(() => expect(screen.queryAllByRole("listitem")).toHaveLength(0));
|
||||
|
||||
// Pin the first message
|
||||
await addLocalPinEvent(pin1);
|
||||
await waitFor(() => expect(screen.queryAllByRole("listitem")).toHaveLength(1));
|
||||
|
||||
// Pin the second message
|
||||
await addNonLocalPinEvent(pin2);
|
||||
await waitFor(() => expect(screen.queryAllByRole("listitem")).toHaveLength(2));
|
||||
});
|
||||
|
||||
it("should updates when messages are unpinned", async () => {
|
||||
// Start with two pins
|
||||
const { removeLastLocalPinEvent, removeLastNonLocalPinEvent } = await initPinnedMessagesCard([pin1], [pin2]);
|
||||
await waitFor(() => expect(screen.queryAllByRole("listitem")).toHaveLength(2));
|
||||
|
||||
// Unpin the first message
|
||||
await removeLastLocalPinEvent();
|
||||
await waitFor(() => expect(screen.queryAllByRole("listitem")).toHaveLength(1));
|
||||
|
||||
// Unpin the second message
|
||||
await removeLastNonLocalPinEvent();
|
||||
await waitFor(() => expect(screen.queryAllByRole("listitem")).toHaveLength(0));
|
||||
});
|
||||
|
||||
it("should display an edited pinned event", async () => {
|
||||
const messageEvent = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
room: "!room:example.org",
|
||||
user: "@alice:example.org",
|
||||
content: {
|
||||
"msgtype": MsgType.Text,
|
||||
"body": " * First pinned message, edited",
|
||||
"m.new_content": {
|
||||
msgtype: MsgType.Text,
|
||||
body: "First pinned message, edited",
|
||||
},
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Replace,
|
||||
event_id: pin1.getId(),
|
||||
},
|
||||
},
|
||||
});
|
||||
cli.relations.mockResolvedValue({
|
||||
originalEvent: pin1,
|
||||
events: [messageEvent],
|
||||
});
|
||||
|
||||
await initPinnedMessagesCard([], [pin1]);
|
||||
expect(screen.getByText("First pinned message, edited")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("unpinnable event", () => {
|
||||
it("should hide unpinnable events found in local timeline", async () => {
|
||||
// Redacted messages are unpinnable
|
||||
const pin = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomCreate,
|
||||
content: {},
|
||||
room: "!room:example.org",
|
||||
user: "@alice:example.org",
|
||||
});
|
||||
await initPinnedMessagesCard([pin], []);
|
||||
expect(screen.queryAllByRole("listitem")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("hides unpinnable events not found in local timeline", async () => {
|
||||
// Redacted messages are unpinnable
|
||||
const pin = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomCreate,
|
||||
content: {},
|
||||
room: "!room:example.org",
|
||||
user: "@alice:example.org",
|
||||
});
|
||||
await initPinnedMessagesCard([], [pin]);
|
||||
expect(screen.queryAllByRole("listitem")).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unpin all", () => {
|
||||
it("should not allow to unpinall", async () => {
|
||||
const room = mkRoom([pin1], [pin2]);
|
||||
jest.spyOn(
|
||||
room.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
|
||||
"mayClientSendStateEvent",
|
||||
).mockReturnValue(false);
|
||||
|
||||
const { asFragment } = render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<PinnedMessagesCard
|
||||
room={room}
|
||||
onClose={jest.fn()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room, room.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
// Wait a tick for state updates
|
||||
await act(() => sleep(0));
|
||||
|
||||
expect(screen.queryByText("Unpin all messages")).toBeNull();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should allow unpinning all messages", async () => {
|
||||
jest.spyOn(Modal, "createDialog");
|
||||
|
||||
const { room } = await initPinnedMessagesCard([pin1], [pin2]);
|
||||
expect(screen.getByText("Unpin all messages")).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByText("Unpin all messages"));
|
||||
// Should open the UnpinAllDialog dialog
|
||||
expect(Modal.createDialog).toHaveBeenCalledWith(UnpinAllDialog, { roomId: room.roomId, matrixClient: cli });
|
||||
});
|
||||
});
|
||||
|
||||
it("should displays votes on polls not found in local timeline", async () => {
|
||||
const poll = mkEvent({
|
||||
...PollStartEvent.from("A poll", ["Option 1", "Option 2"], M_POLL_KIND_DISCLOSED).serialize(),
|
||||
event: true,
|
||||
room: "!room:example.org",
|
||||
user: "@alice:example.org",
|
||||
});
|
||||
|
||||
const answers = (poll.unstableExtensibleEvent as PollStartEvent).answers;
|
||||
const responses = [
|
||||
["@alice:example.org", 0] as [string, number],
|
||||
["@bob:example.org", 0] as [string, number],
|
||||
["@eve:example.org", 1] as [string, number],
|
||||
].map(([user, option], i) =>
|
||||
mkEvent({
|
||||
...PollResponseEvent.from([answers[option as number].id], poll.getId()!).serialize(),
|
||||
event: true,
|
||||
room: "!room:example.org",
|
||||
user,
|
||||
}),
|
||||
);
|
||||
|
||||
const end = mkEvent({
|
||||
...PollEndEvent.from(poll.getId()!, "Closing the poll").serialize(),
|
||||
event: true,
|
||||
room: "!room:example.org",
|
||||
user: "@alice:example.org",
|
||||
});
|
||||
|
||||
// Make the responses available
|
||||
cli.relations.mockImplementation(async (roomId, eventId, relationType, eventType, opts) => {
|
||||
if (eventId === poll.getId() && relationType === RelationType.Reference) {
|
||||
// Paginate the results, for added challenge
|
||||
return opts?.from === "page2"
|
||||
? { originalEvent: poll, events: responses.slice(2) }
|
||||
: { originalEvent: poll, events: [...responses.slice(0, 2), end], nextBatch: "page2" };
|
||||
}
|
||||
// type does not allow originalEvent to be falsy
|
||||
// but code seems to
|
||||
// so still test that
|
||||
return { originalEvent: undefined as unknown as MatrixEvent, events: [] };
|
||||
});
|
||||
|
||||
const { room } = await initPinnedMessagesCard([], [poll]);
|
||||
|
||||
// two pages of results
|
||||
await flushPromises();
|
||||
await flushPromises();
|
||||
|
||||
const pollInstance = room.polls.get(poll.getId()!);
|
||||
expect(pollInstance).toBeTruthy();
|
||||
|
||||
expect(screen.getByText("A poll")).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText("Option 1")).toBeInTheDocument();
|
||||
expect(screen.getByText("2 votes")).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText("Option 2")).toBeInTheDocument();
|
||||
expect(screen.getByText("1 vote")).toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
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, fireEvent, screen, waitFor } from "jest-matrix-react";
|
||||
import { EventType, MatrixEvent, Room, MatrixClient, JoinRule } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import RoomSummaryCard from "../../../../../src/components/views/right_panel/RoomSummaryCard";
|
||||
import ShareDialog from "../../../../../src/components/views/dialogs/ShareDialog";
|
||||
import ExportDialog from "../../../../../src/components/views/dialogs/ExportDialog";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import * as settingsHooks from "../../../../../src/hooks/useSettings";
|
||||
import Modal from "../../../../../src/Modal";
|
||||
import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelStore";
|
||||
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases";
|
||||
import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../../test-utils";
|
||||
import { PollHistoryDialog } from "../../../../../src/components/views/dialogs/PollHistoryDialog";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
import { _t } from "../../../../../src/languageHandler";
|
||||
import { tagRoom } from "../../../../../src/utils/room/tagRoom";
|
||||
import { DefaultTagID } from "../../../../../src/stores/room-list/models";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
|
||||
jest.mock("../../../../../src/utils/room/tagRoom");
|
||||
|
||||
describe("<RoomSummaryCard />", () => {
|
||||
const userId = "@alice:domain.org";
|
||||
|
||||
const roomId = "!room:domain.org";
|
||||
let mockClient!: MockedObject<MatrixClient>;
|
||||
let room!: Room;
|
||||
|
||||
const getComponent = (props = {}) => {
|
||||
const defaultProps = {
|
||||
room,
|
||||
onClose: jest.fn(),
|
||||
permalinkCreator: new RoomPermalinkCreator(room),
|
||||
};
|
||||
|
||||
return render(<RoomSummaryCard {...defaultProps} {...props} />, {
|
||||
wrapper: ({ children }) => (
|
||||
<MatrixClientContext.Provider value={mockClient}>{children}</MatrixClientContext.Provider>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsUser(userId),
|
||||
getAccountData: jest.fn(),
|
||||
isRoomEncrypted: jest.fn(),
|
||||
getOrCreateFilter: jest.fn().mockResolvedValue({ filterId: 1 }),
|
||||
getRoom: jest.fn(),
|
||||
isGuest: jest.fn().mockReturnValue(false),
|
||||
deleteRoomTag: jest.fn().mockResolvedValue({}),
|
||||
setRoomTag: jest.fn().mockResolvedValue({}),
|
||||
});
|
||||
room = new Room(roomId, mockClient, userId);
|
||||
const roomCreateEvent = new MatrixEvent({
|
||||
type: "m.room.create",
|
||||
room_id: roomId,
|
||||
sender: userId,
|
||||
content: {
|
||||
creator: userId,
|
||||
room_version: "5",
|
||||
},
|
||||
state_key: "",
|
||||
});
|
||||
room.currentState.setStateEvents([roomCreateEvent]);
|
||||
room.updateMyMembership(KnownMembership.Join);
|
||||
|
||||
jest.spyOn(Modal, "createDialog");
|
||||
jest.spyOn(RightPanelStore.instance, "pushCard");
|
||||
jest.spyOn(settingsHooks, "useFeatureEnabled").mockReturnValue(false);
|
||||
jest.spyOn(defaultDispatcher, "dispatch");
|
||||
jest.clearAllMocks();
|
||||
DMRoomMap.makeShared(mockClient);
|
||||
|
||||
mockClient.getRoom.mockReturnValue(room);
|
||||
jest.spyOn(room, "isElementVideoRoom").mockRestore();
|
||||
jest.spyOn(room, "isCallRoom").mockRestore();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("renders the room summary", () => {
|
||||
const { container } = getComponent();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders the room topic in the summary", () => {
|
||||
room.currentState.setStateEvents([
|
||||
new MatrixEvent({
|
||||
type: "m.room.topic",
|
||||
room_id: roomId,
|
||||
sender: userId,
|
||||
content: {
|
||||
topic: "This is the room's topic.",
|
||||
},
|
||||
state_key: "",
|
||||
}),
|
||||
]);
|
||||
const { container } = getComponent();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("has button to edit topic", () => {
|
||||
room.currentState.setStateEvents([
|
||||
new MatrixEvent({
|
||||
type: "m.room.topic",
|
||||
room_id: roomId,
|
||||
sender: userId,
|
||||
content: {
|
||||
topic: "This is the room's topic.",
|
||||
},
|
||||
state_key: "",
|
||||
}),
|
||||
]);
|
||||
const { container, getByText } = getComponent();
|
||||
expect(getByText("Edit")).toBeInTheDocument();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("search", () => {
|
||||
it("has the search field", async () => {
|
||||
const onSearchChange = jest.fn();
|
||||
const { getByPlaceholderText } = getComponent({
|
||||
onSearchChange,
|
||||
});
|
||||
expect(getByPlaceholderText("Search messages…")).toBeVisible();
|
||||
});
|
||||
|
||||
it("should focus the search field if Action.FocusMessageSearch is fired", async () => {
|
||||
const onSearchChange = jest.fn();
|
||||
const { getByPlaceholderText } = getComponent({
|
||||
onSearchChange,
|
||||
});
|
||||
expect(getByPlaceholderText("Search messages…")).not.toHaveFocus();
|
||||
defaultDispatcher.fire(Action.FocusMessageSearch);
|
||||
await waitFor(() => {
|
||||
expect(getByPlaceholderText("Search messages…")).toHaveFocus();
|
||||
});
|
||||
});
|
||||
|
||||
it("should focus the search field if focusRoomSearch=true", () => {
|
||||
const onSearchChange = jest.fn();
|
||||
const { getByPlaceholderText } = getComponent({
|
||||
onSearchChange,
|
||||
focusRoomSearch: true,
|
||||
});
|
||||
expect(getByPlaceholderText("Search messages…")).toHaveFocus();
|
||||
});
|
||||
|
||||
it("should cancel search on escape", () => {
|
||||
const onSearchChange = jest.fn();
|
||||
const onSearchCancel = jest.fn();
|
||||
const { getByPlaceholderText } = getComponent({
|
||||
onSearchChange,
|
||||
onSearchCancel,
|
||||
focusRoomSearch: true,
|
||||
});
|
||||
expect(getByPlaceholderText("Search messages…")).toHaveFocus();
|
||||
fireEvent.keyDown(getByPlaceholderText("Search messages…"), { key: "Escape" });
|
||||
expect(onSearchCancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should empty search field when the timeline rendering type changes away", async () => {
|
||||
const onSearchChange = jest.fn();
|
||||
const { rerender } = render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={{ timelineRenderingType: TimelineRenderingType.Search } as any}>
|
||||
<RoomSummaryCard
|
||||
room={room}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
onSearchChange={onSearchChange}
|
||||
focusRoomSearch={true}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
await userEvent.type(screen.getByPlaceholderText("Search messages…"), "test");
|
||||
expect(screen.getByPlaceholderText("Search messages…")).toHaveValue("test");
|
||||
|
||||
rerender(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={{ timelineRenderingType: TimelineRenderingType.Room } as any}>
|
||||
<RoomSummaryCard
|
||||
room={room}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
onSearchChange={onSearchChange}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
expect(screen.getByPlaceholderText("Search messages…")).toHaveValue("");
|
||||
});
|
||||
});
|
||||
|
||||
it("opens room file panel on button click", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
fireEvent.click(getByText("Files"));
|
||||
|
||||
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith({ phase: RightPanelPhases.FilePanel }, true);
|
||||
});
|
||||
|
||||
it("opens room export dialog on button click", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
fireEvent.click(getByText(_t("export_chat|title")));
|
||||
|
||||
expect(Modal.createDialog).toHaveBeenCalledWith(ExportDialog, { room });
|
||||
});
|
||||
|
||||
it("opens share room dialog on button click", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
fireEvent.click(getByText(_t("action|copy_link")));
|
||||
|
||||
expect(Modal.createDialog).toHaveBeenCalledWith(ShareDialog, { target: room });
|
||||
});
|
||||
|
||||
it("opens invite dialog on button click", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
fireEvent.click(getByText(_t("action|invite")));
|
||||
|
||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "view_invite", roomId: room.roomId });
|
||||
});
|
||||
|
||||
it("fires favourite dispatch on button click", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
fireEvent.click(getByText(_t("room|context_menu|favourite")));
|
||||
|
||||
expect(tagRoom).toHaveBeenCalledWith(room, DefaultTagID.Favourite);
|
||||
});
|
||||
|
||||
it("opens room settings on button click", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
fireEvent.click(getByText(_t("common|settings")));
|
||||
|
||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: "open_room_settings" });
|
||||
});
|
||||
|
||||
it("opens room member list on button click", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
fireEvent.click(getByText("People"));
|
||||
|
||||
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith(
|
||||
{ phase: RightPanelPhases.RoomMemberList },
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("opens room threads list on button click", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
fireEvent.click(getByText("Threads"));
|
||||
|
||||
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith({ phase: RightPanelPhases.ThreadPanel }, true);
|
||||
});
|
||||
|
||||
it("opens room pinned messages on button click", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
fireEvent.click(getByText("Pinned messages"));
|
||||
|
||||
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith(
|
||||
{ phase: RightPanelPhases.PinnedMessages },
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
describe("pinning", () => {
|
||||
it("renders pins options", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
expect(getByText("Pinned messages")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("poll history", () => {
|
||||
it("renders poll history option", () => {
|
||||
const { getByText } = getComponent();
|
||||
|
||||
expect(getByText("Polls")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("opens poll history dialog on button click", () => {
|
||||
const permalinkCreator = new RoomPermalinkCreator(room);
|
||||
const { getByText } = getComponent({ permalinkCreator });
|
||||
|
||||
fireEvent.click(getByText("Polls"));
|
||||
|
||||
expect(Modal.createDialog).toHaveBeenCalledWith(PollHistoryDialog, {
|
||||
room,
|
||||
matrixClient: mockClient,
|
||||
permalinkCreator: permalinkCreator,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("video rooms", () => {
|
||||
it("does not render irrelevant options for element video room", () => {
|
||||
jest.spyOn(room, "isElementVideoRoom").mockReturnValue(true);
|
||||
mocked(settingsHooks.useFeatureEnabled).mockImplementation((feature) => feature === "feature_video_rooms");
|
||||
const { queryByText } = getComponent();
|
||||
|
||||
// options not rendered
|
||||
expect(queryByText("Files")).not.toBeInTheDocument();
|
||||
expect(queryByText("Pinned")).not.toBeInTheDocument();
|
||||
expect(queryByText("Export chat")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render irrelevant options for element call room", () => {
|
||||
jest.spyOn(room, "isCallRoom").mockReturnValue(true);
|
||||
mocked(settingsHooks.useFeatureEnabled).mockImplementation(
|
||||
(feature) => feature === "feature_element_call_video_rooms" || feature === "feature_video_rooms",
|
||||
);
|
||||
const { queryByText } = getComponent();
|
||||
|
||||
// options not rendered
|
||||
expect(queryByText("Files")).not.toBeInTheDocument();
|
||||
expect(queryByText("Pinned")).not.toBeInTheDocument();
|
||||
expect(queryByText("Export chat")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("public room label", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(room.currentState, "getJoinRule").mockReturnValue(JoinRule.Public);
|
||||
});
|
||||
|
||||
it("does not show public room label for a DM", async () => {
|
||||
mockClient.getAccountData.mockImplementation(
|
||||
(eventType) =>
|
||||
({
|
||||
[EventType.Direct]: new MatrixEvent({
|
||||
type: EventType.Direct,
|
||||
content: {
|
||||
"@bob:sesame.st": ["some-room-id"],
|
||||
// this room is a DM with ernie
|
||||
"@ernie:sesame.st": ["some-other-room-id", room.roomId],
|
||||
},
|
||||
}),
|
||||
})[eventType],
|
||||
);
|
||||
getComponent();
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(screen.queryByText("Public room")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show public room label for non public room", async () => {
|
||||
jest.spyOn(room.currentState, "getJoinRule").mockReturnValue(JoinRule.Invite);
|
||||
getComponent();
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(screen.queryByText("Public room")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows a public room label for a public room", async () => {
|
||||
jest.spyOn(room.currentState, "getJoinRule").mockReturnValue(JoinRule.Public);
|
||||
getComponent();
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(screen.queryByText("Public room")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
1616
test/unit-tests/components/views/right_panel/UserInfo-test.tsx
Normal file
1616
test/unit-tests/components/views/right_panel/UserInfo-test.tsx
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
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 { act, render, waitFor } from "jest-matrix-react";
|
||||
import React, { ComponentProps } from "react";
|
||||
import { User, TypedEventEmitter, Device, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked, Mocked } from "jest-mock";
|
||||
import {
|
||||
EmojiMapping,
|
||||
ShowSasCallbacks,
|
||||
VerificationPhase as Phase,
|
||||
VerificationRequest,
|
||||
VerificationRequestEvent,
|
||||
Verifier,
|
||||
VerifierEvent,
|
||||
VerifierEventHandlerMap,
|
||||
} from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import VerificationPanel from "../../../../../src/components/views/right_panel/VerificationPanel";
|
||||
import { flushPromises, stubClient } from "../../../../test-utils";
|
||||
|
||||
describe("<VerificationPanel />", () => {
|
||||
let client: MatrixClient;
|
||||
|
||||
beforeEach(() => {
|
||||
client = stubClient();
|
||||
});
|
||||
|
||||
describe("'Ready' phase (dialog mode)", () => {
|
||||
it("should show a 'Start' button", () => {
|
||||
const container = renderComponent({
|
||||
request: makeMockVerificationRequest({
|
||||
phase: Phase.Ready,
|
||||
}),
|
||||
layout: "dialog",
|
||||
});
|
||||
container.getByRole("button", { name: "Start" });
|
||||
});
|
||||
|
||||
it("should show a QR code if the other side can scan and QR bytes are calculated", async () => {
|
||||
const request = makeMockVerificationRequest({
|
||||
phase: Phase.Ready,
|
||||
});
|
||||
request.generateQRCode.mockResolvedValue(Buffer.from("test", "utf-8"));
|
||||
const container = renderComponent({
|
||||
request: request,
|
||||
layout: "dialog",
|
||||
});
|
||||
container.getByText("Scan this unique code");
|
||||
// it shows a spinner at first; wait for the update which makes it show the QR code
|
||||
await waitFor(() => {
|
||||
container.getByAltText("QR Code");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("'Ready' phase (regular mode)", () => {
|
||||
it("should show a 'Verify by emoji' button", () => {
|
||||
const container = renderComponent({
|
||||
request: makeMockVerificationRequest({ phase: Phase.Ready }),
|
||||
});
|
||||
container.getByRole("button", { name: "Verify by emoji" });
|
||||
});
|
||||
|
||||
it("should show a QR code if the other side can scan and QR bytes are calculated", async () => {
|
||||
const request = makeMockVerificationRequest({
|
||||
phase: Phase.Ready,
|
||||
});
|
||||
request.generateQRCode.mockResolvedValue(Buffer.from("test", "utf-8"));
|
||||
const container = renderComponent({
|
||||
request: request,
|
||||
member: new User("@other:user"),
|
||||
});
|
||||
container.getByText("Ask @other:user to scan your code:");
|
||||
// it shows a spinner at first; wait for the update which makes it show the QR code
|
||||
await waitFor(() => {
|
||||
container.getByAltText("QR Code");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("'Verify by emoji' flow", () => {
|
||||
let mockVerifier: Mocked<Verifier>;
|
||||
let mockRequest: Mocked<VerificationRequest>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockVerifier = makeMockVerifier();
|
||||
mockRequest = makeMockVerificationRequest({
|
||||
verifier: mockVerifier as unknown as VerificationRequest["verifier"],
|
||||
chosenMethod: "m.sas.v1",
|
||||
});
|
||||
});
|
||||
|
||||
it("shows a spinner initially", () => {
|
||||
const { container } = renderComponent({
|
||||
request: mockRequest,
|
||||
phase: Phase.Started,
|
||||
});
|
||||
expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should show some emojis once keys are exchanged", () => {
|
||||
const { container } = renderComponent({
|
||||
request: mockRequest,
|
||||
phase: Phase.Started,
|
||||
});
|
||||
|
||||
// fire the ShowSas event
|
||||
const sasEvent = makeMockSasCallbacks();
|
||||
mockVerifier.getShowSasCallbacks.mockReturnValue(sasEvent);
|
||||
act(() => {
|
||||
mockVerifier.emit(VerifierEvent.ShowSas, sasEvent);
|
||||
});
|
||||
|
||||
const emojis = container.getElementsByClassName("mx_VerificationShowSas_emojiSas_block");
|
||||
expect(emojis.length).toEqual(7);
|
||||
for (const emoji of emojis) {
|
||||
expect(emoji).toHaveTextContent("🦄Unicorn");
|
||||
}
|
||||
});
|
||||
|
||||
describe("'Verify own device' flow", () => {
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(mockRequest, "isSelfVerification", { get: () => true });
|
||||
Object.defineProperty(mockRequest, "otherDeviceId", { get: () => "other_device" });
|
||||
|
||||
const otherDeviceDetails = new Device({
|
||||
algorithms: [],
|
||||
deviceId: "other_device",
|
||||
keys: new Map(),
|
||||
userId: "",
|
||||
displayName: "my other device",
|
||||
});
|
||||
|
||||
mocked(client.getCrypto()!).getUserDeviceInfo.mockResolvedValue(
|
||||
new Map([[client.getSafeUserId(), new Map([["other_device", otherDeviceDetails]])]]),
|
||||
);
|
||||
});
|
||||
|
||||
it("should show 'Waiting for you to verify' after confirming", async () => {
|
||||
const rendered = renderComponent({
|
||||
request: mockRequest,
|
||||
phase: Phase.Started,
|
||||
});
|
||||
|
||||
// wait for the device to be looked up
|
||||
await act(() => flushPromises());
|
||||
|
||||
// fire the ShowSas event
|
||||
const sasEvent = makeMockSasCallbacks();
|
||||
mockVerifier.getShowSasCallbacks.mockReturnValue(sasEvent);
|
||||
act(() => {
|
||||
mockVerifier.emit(VerifierEvent.ShowSas, sasEvent);
|
||||
});
|
||||
|
||||
// confirm
|
||||
act(() => {
|
||||
rendered.getByRole("button", { name: "They match" }).click();
|
||||
});
|
||||
|
||||
expect(rendered.container).toHaveTextContent(
|
||||
"Waiting for you to verify on your other device, my other device (other_device)…",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function renderComponent(props: Partial<ComponentProps<typeof VerificationPanel>> & { request: VerificationRequest }) {
|
||||
const defaultProps = {
|
||||
layout: "",
|
||||
member: {} as User,
|
||||
onClose: () => {},
|
||||
isRoomEncrypted: false,
|
||||
inDialog: false,
|
||||
phase: props.request.phase,
|
||||
};
|
||||
return render(<VerificationPanel {...defaultProps} {...props} />);
|
||||
}
|
||||
|
||||
function makeMockVerificationRequest(props: Partial<VerificationRequest> = {}): Mocked<VerificationRequest> {
|
||||
const request = new TypedEventEmitter<VerificationRequestEvent, any>();
|
||||
Object.assign(request, {
|
||||
cancel: jest.fn(),
|
||||
otherPartySupportsMethod: jest.fn().mockReturnValue(true),
|
||||
generateQRCode: jest.fn().mockResolvedValue(undefined),
|
||||
...props,
|
||||
});
|
||||
return request as unknown as Mocked<VerificationRequest>;
|
||||
}
|
||||
|
||||
function makeMockVerifier(): Mocked<Verifier> {
|
||||
const verifier = new TypedEventEmitter<VerifierEvent, VerifierEventHandlerMap>();
|
||||
Object.assign(verifier, {
|
||||
cancel: jest.fn(),
|
||||
verify: jest.fn(),
|
||||
getShowSasCallbacks: jest.fn(),
|
||||
getReciprocateQrCodeCallbacks: jest.fn(),
|
||||
});
|
||||
return verifier as unknown as Mocked<Verifier>;
|
||||
}
|
||||
|
||||
function makeMockSasCallbacks(): ShowSasCallbacks {
|
||||
const unicorn: EmojiMapping = ["🦄", "unicorn"];
|
||||
return {
|
||||
sas: {
|
||||
emoji: new Array<EmojiMapping>(7).fill(unicorn),
|
||||
},
|
||||
cancel: jest.fn(),
|
||||
confirm: jest.fn(),
|
||||
mismatch: jest.fn(),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<BaseCard /> should close when clicking X button 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BaseCard"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header_title"
|
||||
>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_BaseCard_header_title_heading"
|
||||
role="heading"
|
||||
>
|
||||
Heading text
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby="floating-ui-1"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 28px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="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.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div>
|
||||
Content
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,285 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<ExtensionsCard /> should render empty state 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BaseCard mx_ExtensionsCard"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header_title"
|
||||
>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_BaseCard_header_title_heading"
|
||||
role="heading"
|
||||
>
|
||||
Extensions
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby="floating-ui-1"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 28px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="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.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar"
|
||||
tabindex="-1"
|
||||
>
|
||||
<button
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11 13H6a.967.967 0 0 1-.713-.287A.968.968 0 0 1 5 12c0-.283.096-.52.287-.713A.967.967 0 0 1 6 11h5V6c0-.283.096-.52.287-.713A.968.968 0 0 1 12 5c.283 0 .52.096.713.287.191.192.287.43.287.713v5h5a.97.97 0 0 1 .712.287c.192.192.288.43.288.713s-.096.52-.288.713A.968.968 0 0 1 18 13h-5v5a.97.97 0 0 1-.287.712A.968.968 0 0 1 12 19a.968.968 0 0 1-.713-.288A.968.968 0 0 1 11 18v-5Z"
|
||||
/>
|
||||
</svg>
|
||||
Add extensions
|
||||
</button>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyState"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-4x);"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="32px"
|
||||
viewBox="0 0 24 24"
|
||||
width="32px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M17.25 11.672a.907.907 0 0 1-.663-.282L12.61 7.413a.907.907 0 0 1-.282-.663c0-.254.094-.475.282-.663l3.977-3.977a.907.907 0 0 1 .663-.282c.254 0 .475.094.663.282l3.977 3.977a.907.907 0 0 1 .282.663.907.907 0 0 1-.282.663l-3.977 3.977a.907.907 0 0 1-.663.282Zm2.475-4.922L17.25 4.275 14.775 6.75l2.475 2.475 2.475-2.475ZM4 11a.967.967 0 0 1-.712-.287A.968.968 0 0 1 3 10V4c0-.283.096-.52.288-.712A.968.968 0 0 1 4 3h6a.97.97 0 0 1 .713.288A.968.968 0 0 1 11 4v6c0 .283-.096.52-.287.713A.968.968 0 0 1 10 11H4Zm5-2V5H5v4h4Zm5 12a.968.968 0 0 1-.713-.288A.968.968 0 0 1 13 20v-6c0-.283.096-.52.287-.713A.968.968 0 0 1 14 13h6a.97.97 0 0 1 .712.287c.192.192.288.43.288.713v6c0 .283-.096.52-.288.712A.968.968 0 0 1 20 21h-6Zm5-2v-4h-4v4h4ZM4 21a.967.967 0 0 1-.712-.288A.968.968 0 0 1 3 20v-6a.97.97 0 0 1 .288-.713A.967.967 0 0 1 4 13h6c.283 0 .52.096.713.287.191.192.287.43.287.713v6a.97.97 0 0 1-.287.712A.968.968 0 0 1 10 21H4Zm5-2v-4H5v4h4Z"
|
||||
/>
|
||||
</svg>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83"
|
||||
>
|
||||
Boost productivity with more tools, widgets and bots
|
||||
</p>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-regular_yh5dq_59"
|
||||
>
|
||||
Select “Add extensions” to browse and add extensions to this room
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<ExtensionsCard /> should render widgets 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BaseCard mx_ExtensionsCard"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header_title"
|
||||
>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_BaseCard_header_title_heading"
|
||||
role="heading"
|
||||
>
|
||||
Extensions
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby="floating-ui-6"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 28px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="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.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar"
|
||||
tabindex="-1"
|
||||
>
|
||||
<button
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11 13H6a.967.967 0 0 1-.713-.287A.968.968 0 0 1 5 12c0-.283.096-.52.287-.713A.967.967 0 0 1 6 11h5V6c0-.283.096-.52.287-.713A.968.968 0 0 1 12 5c.283 0 .52.096.713.287.191.192.287.43.287.713v5h5a.97.97 0 0 1 .712.287c.192.192.288.43.288.713s-.096.52-.288.713A.968.968 0 0 1 18 13h-5v5a.97.97 0 0 1-.287.712A.968.968 0 0 1 12 19a.968.968 0 0 1-.713-.288A.968.968 0 0 1 11 18v-5Z"
|
||||
/>
|
||||
</svg>
|
||||
Add extensions
|
||||
</button>
|
||||
<div
|
||||
class="_separator_144s5_17"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
<div
|
||||
class="mx_BaseCard_Button mx_ExtensionsCard_Button"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_ExtensionsCard_icon_app"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
aria-label="Avatar"
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_WidgetAvatar"
|
||||
data-color="1"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
style="--cpd-avatar-size: 24px;"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="_image_mcap2_50"
|
||||
data-type="round"
|
||||
height="24px"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
src="image-file-stub"
|
||||
width="24px"
|
||||
/>
|
||||
</span>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_lineClamp"
|
||||
>
|
||||
Custom Widget
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Pin"
|
||||
class="mx_AccessibleButton mx_ExtensionsCard_app_pinToggle"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mx_BaseCard_Button mx_ExtensionsCard_Button"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_ExtensionsCard_icon_app"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
aria-label="Avatar"
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_WidgetAvatar"
|
||||
data-color="1"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
style="--cpd-avatar-size: 24px;"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="_image_mcap2_50"
|
||||
data-type="round"
|
||||
height="24px"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
src="image-file-stub"
|
||||
width="24px"
|
||||
/>
|
||||
</span>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_lineClamp"
|
||||
>
|
||||
Jitsi
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Pin"
|
||||
class="mx_AccessibleButton mx_ExtensionsCard_app_pinToggle"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<ExtensionsCard /> should show context menu on widget row 1`] = `
|
||||
<ul
|
||||
class="mx_IconizedContextMenu"
|
||||
role="none"
|
||||
>
|
||||
<div
|
||||
class="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst"
|
||||
>
|
||||
<li
|
||||
aria-label="Remove for everyone"
|
||||
class="mx_AccessibleButton mx_IconizedContextMenu_item"
|
||||
role="menuitem"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="mx_IconizedContextMenu_label"
|
||||
>
|
||||
Remove for everyone
|
||||
</span>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
`;
|
|
@ -0,0 +1,507 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<PinnedMessagesCard /> should show the empty state when there are no pins 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BaseCard mx_PinnedMessagesCard"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header_title"
|
||||
>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_BaseCard_header_title_heading"
|
||||
role="heading"
|
||||
>
|
||||
Pinned messages
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby="floating-ui-12"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 28px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="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.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_Flex mx_EmptyState"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-4x);"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="32px"
|
||||
viewBox="0 0 24 24"
|
||||
width="32px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M6.119 2a.5.5 0 0 0-.35.857L7.85 4.9a.5.5 0 0 1 .15.357v4.487a.5.5 0 0 1-.15.356l-3.7 3.644A.5.5 0 0 0 4 14.1v1.4a.5.5 0 0 0 .5.5H11v6a1 1 0 1 0 2 0v-6h6.5a.5.5 0 0 0 .5-.5v-1.4a.5.5 0 0 0-.15-.356l-3.7-3.644a.5.5 0 0 1-.15-.356V5.257a.5.5 0 0 1 .15-.357l2.081-2.043a.5.5 0 0 0-.35-.857H6.119ZM10 4h4v5.744a2.5 2.5 0 0 0 .746 1.781L17.26 14H6.74l2.514-2.475A2.5 2.5 0 0 0 10 9.744V4Z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83"
|
||||
>
|
||||
Pin important messages so that they can be easily discovered
|
||||
</p>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-regular_yh5dq_59"
|
||||
>
|
||||
Select a message and choose “Pin” to it include here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<PinnedMessagesCard /> should show two pinned messages 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BaseCard mx_PinnedMessagesCard"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header_title"
|
||||
>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_BaseCard_header_title_heading"
|
||||
role="heading"
|
||||
>
|
||||
2 Pinned messages
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby="floating-ui-18"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 28px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="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.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_PinnedMessagesCard_wrapper mx_PinnedMessagesCard_wrapper_unpin_all"
|
||||
role="list"
|
||||
>
|
||||
<div
|
||||
class="mx_PinnedEventTile"
|
||||
role="listitem"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_PinnedEventTile_senderAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
a
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_PinnedEventTile_wrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_PinnedEventTile_top"
|
||||
>
|
||||
<span
|
||||
aria-labelledby="floating-ui-24"
|
||||
class="mx_PinnedEventTile_sender mx_Username_color3"
|
||||
>
|
||||
@alice:example.org
|
||||
</span>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Open menu"
|
||||
class="_icon-button_bh2qc_17"
|
||||
data-state="closed"
|
||||
id="radix-2"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 24px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="M6 14c-.55 0-1.02-.196-1.412-.588A1.926 1.926 0 0 1 4 12c0-.55.196-1.02.588-1.412A1.926 1.926 0 0 1 6 10c.55 0 1.02.196 1.412.588.392.391.588.862.588 1.412 0 .55-.196 1.02-.588 1.412A1.926 1.926 0 0 1 6 14Zm6 0c-.55 0-1.02-.196-1.412-.588A1.926 1.926 0 0 1 10 12c0-.55.196-1.02.588-1.412A1.926 1.926 0 0 1 12 10c.55 0 1.02.196 1.412.588.392.391.588.862.588 1.412 0 .55-.196 1.02-.588 1.412A1.926 1.926 0 0 1 12 14Zm6 0c-.55 0-1.02-.196-1.413-.588A1.926 1.926 0 0 1 16 12c0-.55.196-1.02.587-1.412A1.926 1.926 0 0 1 18 10c.55 0 1.02.196 1.413.588.391.391.587.862.587 1.412 0 .55-.196 1.02-.587 1.412A1.926 1.926 0 0 1 18 14Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<div
|
||||
class="mx_EventTile_body translate"
|
||||
dir="auto"
|
||||
>
|
||||
The second one
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_144s5_17 mx_PinnedMessagesCard_Separator"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
<div
|
||||
class="mx_PinnedEventTile"
|
||||
role="listitem"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_PinnedEventTile_senderAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
a
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_PinnedEventTile_wrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_PinnedEventTile_top"
|
||||
>
|
||||
<span
|
||||
aria-labelledby="floating-ui-29"
|
||||
class="mx_PinnedEventTile_sender mx_Username_color3"
|
||||
>
|
||||
@alice:example.org
|
||||
</span>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Open menu"
|
||||
class="_icon-button_bh2qc_17"
|
||||
data-state="closed"
|
||||
id="radix-3"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 24px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="M6 14c-.55 0-1.02-.196-1.412-.588A1.926 1.926 0 0 1 4 12c0-.55.196-1.02.588-1.412A1.926 1.926 0 0 1 6 10c.55 0 1.02.196 1.412.588.392.391.588.862.588 1.412 0 .55-.196 1.02-.588 1.412A1.926 1.926 0 0 1 6 14Zm6 0c-.55 0-1.02-.196-1.412-.588A1.926 1.926 0 0 1 10 12c0-.55.196-1.02.588-1.412A1.926 1.926 0 0 1 12 10c.55 0 1.02.196 1.412.588.392.391.588.862.588 1.412 0 .55-.196 1.02-.588 1.412A1.926 1.926 0 0 1 12 14Zm6 0c-.55 0-1.02-.196-1.413-.588A1.926 1.926 0 0 1 16 12c0-.55.196-1.02.587-1.412A1.926 1.926 0 0 1 18 10c.55 0 1.02.196 1.413.588.391.391.587.862.587 1.412 0 .55-.196 1.02-.587 1.412A1.926 1.926 0 0 1 18 14Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<div
|
||||
class="mx_EventTile_body translate"
|
||||
dir="auto"
|
||||
>
|
||||
First pinned message
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_PinnedMessagesCard_unpin"
|
||||
>
|
||||
<button
|
||||
class="_button_i91xf_17"
|
||||
data-kind="tertiary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Unpin all messages
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<PinnedMessagesCard /> unpin all should not allow to unpinall 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_BaseCard mx_PinnedMessagesCard"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header_title"
|
||||
>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_BaseCard_header_title_heading"
|
||||
role="heading"
|
||||
>
|
||||
2 Pinned messages
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby="floating-ui-712"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 28px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="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.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_PinnedMessagesCard_wrapper"
|
||||
role="list"
|
||||
>
|
||||
<div
|
||||
class="mx_PinnedEventTile"
|
||||
role="listitem"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_PinnedEventTile_senderAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
a
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_PinnedEventTile_wrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_PinnedEventTile_top"
|
||||
>
|
||||
<span
|
||||
aria-labelledby="floating-ui-718"
|
||||
class="mx_PinnedEventTile_sender mx_Username_color3"
|
||||
>
|
||||
@alice:example.org
|
||||
</span>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Open menu"
|
||||
class="_icon-button_bh2qc_17"
|
||||
data-state="closed"
|
||||
id="radix-218"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 24px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="M6 14c-.55 0-1.02-.196-1.412-.588A1.926 1.926 0 0 1 4 12c0-.55.196-1.02.588-1.412A1.926 1.926 0 0 1 6 10c.55 0 1.02.196 1.412.588.392.391.588.862.588 1.412 0 .55-.196 1.02-.588 1.412A1.926 1.926 0 0 1 6 14Zm6 0c-.55 0-1.02-.196-1.412-.588A1.926 1.926 0 0 1 10 12c0-.55.196-1.02.588-1.412A1.926 1.926 0 0 1 12 10c.55 0 1.02.196 1.412.588.392.391.588.862.588 1.412 0 .55-.196 1.02-.588 1.412A1.926 1.926 0 0 1 12 14Zm6 0c-.55 0-1.02-.196-1.413-.588A1.926 1.926 0 0 1 16 12c0-.55.196-1.02.587-1.412A1.926 1.926 0 0 1 18 10c.55 0 1.02.196 1.413.588.391.391.587.862.587 1.412 0 .55-.196 1.02-.587 1.412A1.926 1.926 0 0 1 18 14Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<div
|
||||
class="mx_EventTile_body translate"
|
||||
dir="auto"
|
||||
>
|
||||
The second one
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_144s5_17 mx_PinnedMessagesCard_Separator"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
<div
|
||||
class="mx_PinnedEventTile"
|
||||
role="listitem"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_PinnedEventTile_senderAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
a
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_PinnedEventTile_wrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_PinnedEventTile_top"
|
||||
>
|
||||
<span
|
||||
aria-labelledby="floating-ui-723"
|
||||
class="mx_PinnedEventTile_sender mx_Username_color3"
|
||||
>
|
||||
@alice:example.org
|
||||
</span>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Open menu"
|
||||
class="_icon-button_bh2qc_17"
|
||||
data-state="closed"
|
||||
id="radix-219"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 24px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="M6 14c-.55 0-1.02-.196-1.412-.588A1.926 1.926 0 0 1 4 12c0-.55.196-1.02.588-1.412A1.926 1.926 0 0 1 6 10c.55 0 1.02.196 1.412.588.392.391.588.862.588 1.412 0 .55-.196 1.02-.588 1.412A1.926 1.926 0 0 1 6 14Zm6 0c-.55 0-1.02-.196-1.412-.588A1.926 1.926 0 0 1 10 12c0-.55.196-1.02.588-1.412A1.926 1.926 0 0 1 12 10c.55 0 1.02.196 1.412.588.392.391.588.862.588 1.412 0 .55-.196 1.02-.588 1.412A1.926 1.926 0 0 1 12 14Zm6 0c-.55 0-1.02-.196-1.413-.588A1.926 1.926 0 0 1 16 12c0-.55.196-1.02.587-1.412A1.926 1.926 0 0 1 18 10c.55 0 1.02.196 1.413.588.391.391.587.862.587 1.412 0 .55-.196 1.02-.587 1.412A1.926 1.926 0 0 1 18 14Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<div
|
||||
class="mx_EventTile_body translate"
|
||||
dir="auto"
|
||||
>
|
||||
First pinned message
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,739 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<DeviceItem /> ambiguous display name 1`] = `
|
||||
<div>
|
||||
<div
|
||||
aria-label="my display name (deviceId)"
|
||||
class="mx_AccessibleButton mx_UserInfo_device mx_UserInfo_device_unverified"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_normal"
|
||||
/>
|
||||
<div
|
||||
class="mx_UserInfo_device_name"
|
||||
>
|
||||
my display name (deviceId)
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_device_trusted"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<DeviceItem /> with display name 1`] = `
|
||||
<div>
|
||||
<div
|
||||
aria-label="deviceName"
|
||||
class="mx_AccessibleButton mx_UserInfo_device mx_UserInfo_device_unverified"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_normal"
|
||||
/>
|
||||
<div
|
||||
class="mx_UserInfo_device_name"
|
||||
>
|
||||
deviceName
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_device_trusted"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<DeviceItem /> without display name 1`] = `
|
||||
<div>
|
||||
<div
|
||||
aria-label="deviceId"
|
||||
class="mx_AccessibleButton mx_UserInfo_device mx_UserInfo_device_unverified"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_normal"
|
||||
/>
|
||||
<div
|
||||
class="mx_UserInfo_device_name"
|
||||
>
|
||||
deviceId
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_device_trusted"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<UserInfo /> with crypto enabled renders <BasicUserInfo /> 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_BaseCard mx_UserInfo"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header_title"
|
||||
>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_BaseCard_header_title_heading"
|
||||
role="heading"
|
||||
>
|
||||
Profile
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby="floating-ui-220"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 28px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="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.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_UserInfo_avatar"
|
||||
>
|
||||
<div
|
||||
class="mx_UserInfo_avatar_transition"
|
||||
>
|
||||
<div
|
||||
class="mx_UserInfo_avatar_transition_child"
|
||||
>
|
||||
<button
|
||||
aria-label="Profile picture"
|
||||
aria-live="off"
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="button"
|
||||
style="--cpd-avatar-size: 120px;"
|
||||
>
|
||||
u
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_container"
|
||||
>
|
||||
<div
|
||||
class="mx_Flex mx_UserInfo_profile"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
|
||||
>
|
||||
<h1
|
||||
class="_typography_yh5dq_162 _font-heading-sm-semibold_yh5dq_102"
|
||||
dir="auto"
|
||||
>
|
||||
<div
|
||||
class="mx_Flex"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row-reverse; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
|
||||
>
|
||||
@user:example.com
|
||||
</div>
|
||||
</h1>
|
||||
<div
|
||||
class="mx_PresenceLabel mx_UserInfo_profileStatus"
|
||||
>
|
||||
Unknown
|
||||
</div>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-sm-semibold_yh5dq_45 mx_UserInfo_profile_mxid"
|
||||
>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
customUserIdentifier
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_container"
|
||||
>
|
||||
<h2>
|
||||
Security
|
||||
</h2>
|
||||
<p>
|
||||
Messages in this room are not end-to-end encrypted.
|
||||
</p>
|
||||
<div
|
||||
class="mx_UserInfo_container_verifyButton"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_UserInfo_field mx_UserInfo_verifyButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Verify
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_devices"
|
||||
>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_UserInfo_expand mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_normal"
|
||||
/>
|
||||
<div>
|
||||
1 session
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_container"
|
||||
>
|
||||
<button
|
||||
class="_item_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_8j2l6_43"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m1.5 21.25 1.45-4.95a10.232 10.232 0 0 1-.712-2.1A10.167 10.167 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.737 9.737 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.737 9.737 0 0 1 12 22c-.75 0-1.483-.08-2.2-.238a10.23 10.23 0 0 1-2.1-.712L2.75 22.5a.936.936 0 0 1-1-.25.936.936 0 0 1-.25-1Zm2.45-1.2 3.2-.95a.949.949 0 0 1 .275-.063c.1-.008.192-.012.275-.012.15 0 .296.013.438.038.141.024.279.07.412.137a7.435 7.435 0 0 0 1.675.6c.583.133 1.175.2 1.775.2 2.233 0 4.125-.775 5.675-2.325C19.225 16.125 20 14.233 20 12c0-2.233-.775-4.125-2.325-5.675C16.125 4.775 14.233 4 12 4c-2.233 0-4.125.775-5.675 2.325C4.775 7.875 4 9.767 4 12c0 .6.067 1.192.2 1.775s.333 1.142.6 1.675c.117.217.18.446.188.688a2.29 2.29 0 0 1-.088.712l-.95 3.2Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
Send message
|
||||
</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>
|
||||
</button>
|
||||
<button
|
||||
class="_item_8j2l6_17 _interactive_8j2l6_35 _disabled_8j2l6_118"
|
||||
data-kind="primary"
|
||||
disabled=""
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_8j2l6_43"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212L4.55 13c-.183-.183-.27-.42-.263-.713.009-.291.105-.529.288-.712a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275L9.55 15.15l8.475-8.475c.183-.183.42-.275.713-.275.291 0 .529.092.712.275.183.183.275.42.275.713 0 .291-.092.529-.275.712l-9.2 9.2c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
Jump to read receipt
|
||||
</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>
|
||||
</button>
|
||||
<button
|
||||
class="_item_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_8j2l6_43"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 16a.968.968 0 0 1-.713-.287A.967.967 0 0 1 11 15V7.85L9.125 9.725c-.2.2-.433.3-.7.3-.267 0-.508-.108-.725-.325a.93.93 0 0 1-.288-.712A.977.977 0 0 1 7.7 8.3l3.6-3.6c.1-.1.208-.17.325-.212.117-.042.242-.063.375-.063s.258.02.375.063a.877.877 0 0 1 .325.212l3.6 3.6c.2.2.296.438.287.713a.977.977 0 0 1-.287.687c-.2.2-.438.304-.713.313a.93.93 0 0 1-.712-.288L13 7.85V15c0 .283-.096.52-.287.713A.968.968 0 0 1 12 16Zm-6 4c-.55 0-1.02-.196-1.412-.587A1.926 1.926 0 0 1 4 18v-2c0-.283.096-.52.287-.713A.968.968 0 0 1 5 15c.283 0 .52.096.713.287.191.192.287.43.287.713v2h12v-2a.97.97 0 0 1 .288-.713A.968.968 0 0 1 19 15a.97.97 0 0 1 .712.287c.192.192.288.43.288.713v2c0 .55-.196 1.02-.587 1.413A1.926 1.926 0 0 1 18 20H6Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
Share profile
|
||||
</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>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_container"
|
||||
>
|
||||
<button
|
||||
class="_item_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="critical"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_8j2l6_43"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 22a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Zm0-2c2.233 0 4.125-.775 5.675-2.325C19.225 16.125 20 14.233 20 12c0-.9-.146-1.767-.438-2.6A7.951 7.951 0 0 0 18.3 7.1L7.1 18.3c.7.55 1.467.97 2.3 1.262.833.292 1.7.438 2.6.438Zm-6.3-3.1L16.9 5.7a7.95 7.95 0 0 0-2.3-1.263A7.813 7.813 0 0 0 12 4c-2.233 0-4.125.775-5.675 2.325C4.775 7.875 4 9.767 4 12c0 .9.146 1.767.438 2.6A7.95 7.95 0 0 0 5.7 16.9Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
Ignore
|
||||
</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>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<UserInfo /> with crypto enabled should render a deactivate button for users of the same server if we are a server admin 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_BaseCard mx_UserInfo"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header"
|
||||
>
|
||||
<div
|
||||
class="mx_BaseCard_header_title"
|
||||
>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 mx_BaseCard_header_title_heading"
|
||||
role="heading"
|
||||
>
|
||||
Profile
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby="floating-ui-290"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 28px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
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="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.414Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_UserInfo_avatar"
|
||||
>
|
||||
<div
|
||||
class="mx_UserInfo_avatar_transition"
|
||||
>
|
||||
<div
|
||||
class="mx_UserInfo_avatar_transition_child"
|
||||
>
|
||||
<button
|
||||
aria-label="Profile picture"
|
||||
aria-live="off"
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="3"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="button"
|
||||
style="--cpd-avatar-size: 120px;"
|
||||
>
|
||||
u
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_container"
|
||||
>
|
||||
<div
|
||||
class="mx_Flex mx_UserInfo_profile"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
|
||||
>
|
||||
<h1
|
||||
class="_typography_yh5dq_162 _font-heading-sm-semibold_yh5dq_102"
|
||||
dir="auto"
|
||||
>
|
||||
<div
|
||||
class="mx_Flex"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row-reverse; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
|
||||
>
|
||||
@user:example.com
|
||||
</div>
|
||||
</h1>
|
||||
<div
|
||||
class="mx_PresenceLabel mx_UserInfo_profileStatus"
|
||||
>
|
||||
Unknown
|
||||
</div>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-sm-semibold_yh5dq_45 mx_UserInfo_profile_mxid"
|
||||
>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
customUserIdentifier
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_container"
|
||||
>
|
||||
<h2>
|
||||
Security
|
||||
</h2>
|
||||
<p>
|
||||
Messages in this room are not end-to-end encrypted.
|
||||
</p>
|
||||
<div
|
||||
class="mx_UserInfo_container_verifyButton"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_UserInfo_field mx_UserInfo_verifyButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Verify
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_devices"
|
||||
>
|
||||
<div />
|
||||
<div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_UserInfo_expand mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_normal"
|
||||
/>
|
||||
<div>
|
||||
1 session
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_container"
|
||||
>
|
||||
<button
|
||||
class="_item_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_8j2l6_43"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m1.5 21.25 1.45-4.95a10.232 10.232 0 0 1-.712-2.1A10.167 10.167 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.737 9.737 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.737 9.737 0 0 1 12 22c-.75 0-1.483-.08-2.2-.238a10.23 10.23 0 0 1-2.1-.712L2.75 22.5a.936.936 0 0 1-1-.25.936.936 0 0 1-.25-1Zm2.45-1.2 3.2-.95a.949.949 0 0 1 .275-.063c.1-.008.192-.012.275-.012.15 0 .296.013.438.038.141.024.279.07.412.137a7.435 7.435 0 0 0 1.675.6c.583.133 1.175.2 1.775.2 2.233 0 4.125-.775 5.675-2.325C19.225 16.125 20 14.233 20 12c0-2.233-.775-4.125-2.325-5.675C16.125 4.775 14.233 4 12 4c-2.233 0-4.125.775-5.675 2.325C4.775 7.875 4 9.767 4 12c0 .6.067 1.192.2 1.775s.333 1.142.6 1.675c.117.217.18.446.188.688a2.29 2.29 0 0 1-.088.712l-.95 3.2Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
Send message
|
||||
</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>
|
||||
</button>
|
||||
<button
|
||||
class="_item_8j2l6_17 _interactive_8j2l6_35 _disabled_8j2l6_118"
|
||||
data-kind="primary"
|
||||
disabled=""
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_8j2l6_43"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212L4.55 13c-.183-.183-.27-.42-.263-.713.009-.291.105-.529.288-.712a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275L9.55 15.15l8.475-8.475c.183-.183.42-.275.713-.275.291 0 .529.092.712.275.183.183.275.42.275.713 0 .291-.092.529-.275.712l-9.2 9.2c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
Jump to read receipt
|
||||
</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>
|
||||
</button>
|
||||
<button
|
||||
class="_item_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="primary"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_8j2l6_43"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 16a.968.968 0 0 1-.713-.287A.967.967 0 0 1 11 15V7.85L9.125 9.725c-.2.2-.433.3-.7.3-.267 0-.508-.108-.725-.325a.93.93 0 0 1-.288-.712A.977.977 0 0 1 7.7 8.3l3.6-3.6c.1-.1.208-.17.325-.212.117-.042.242-.063.375-.063s.258.02.375.063a.877.877 0 0 1 .325.212l3.6 3.6c.2.2.296.438.287.713a.977.977 0 0 1-.287.687c-.2.2-.438.304-.713.313a.93.93 0 0 1-.712-.288L13 7.85V15c0 .283-.096.52-.287.713A.968.968 0 0 1 12 16Zm-6 4c-.55 0-1.02-.196-1.412-.587A1.926 1.926 0 0 1 4 18v-2c0-.283.096-.52.287-.713A.968.968 0 0 1 5 15c.283 0 .52.096.713.287.191.192.287.43.287.713v2h12v-2a.97.97 0 0 1 .288-.713A.968.968 0 0 1 19 15a.97.97 0 0 1 .712.287c.192.192.288.43.288.713v2c0 .55-.196 1.02-.587 1.413A1.926 1.926 0 0 1 18 20H6Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
Share profile
|
||||
</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>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_container"
|
||||
>
|
||||
<button
|
||||
class="_item_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="critical"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_8j2l6_43"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7 21c-.55 0-1.02-.196-1.412-.587A1.926 1.926 0 0 1 5 19V6a.968.968 0 0 1-.713-.287A.968.968 0 0 1 4 5c0-.283.096-.52.287-.713A.968.968 0 0 1 5 4h4a.97.97 0 0 1 .287-.712A.968.968 0 0 1 10 3h4a.97.97 0 0 1 .713.288A.968.968 0 0 1 15 4h4a.97.97 0 0 1 .712.287c.192.192.288.43.288.713s-.096.52-.288.713A.968.968 0 0 1 19 6v13c0 .55-.196 1.02-.587 1.413A1.926 1.926 0 0 1 17 21H7ZM7 6v13h10V6H7Zm2 10c0 .283.096.52.287.712.192.192.43.288.713.288s.52-.096.713-.288A.968.968 0 0 0 11 16V9a.967.967 0 0 0-.287-.713A.968.968 0 0 0 10 8a.968.968 0 0 0-.713.287A.968.968 0 0 0 9 9v7Zm4 0c0 .283.096.52.287.712.192.192.43.288.713.288s.52-.096.713-.288A.968.968 0 0 0 15 16V9a.967.967 0 0 0-.287-.713A.968.968 0 0 0 14 8a.968.968 0 0 0-.713.287A.967.967 0 0 0 13 9v7Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
Deactivate user
|
||||
</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>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_UserInfo_container"
|
||||
>
|
||||
<button
|
||||
class="_item_8j2l6_17 _interactive_8j2l6_35"
|
||||
data-kind="critical"
|
||||
role="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_icon_8j2l6_43"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 22a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Zm0-2c2.233 0 4.125-.775 5.675-2.325C19.225 16.125 20 14.233 20 12c0-.9-.146-1.767-.438-2.6A7.951 7.951 0 0 0 18.3 7.1L7.1 18.3c.7.55 1.467.97 2.3 1.262.833.292 1.7.438 2.6.438Zm-6.3-3.1L16.9 5.7a7.95 7.95 0 0 0-2.3-1.263A7.813 7.813 0 0 0 12 4c-2.233 0-4.125.775-5.675 2.325C4.775 7.875 4 9.767 4 12c0 .9.146 1.767.438 2.6A7.95 7.95 0 0 0 5.7 16.9Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-medium_yh5dq_69 _label_8j2l6_52"
|
||||
>
|
||||
Ignore
|
||||
</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>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
Loading…
Add table
Add a link
Reference in a new issue