Add a devtool for looking at users and their devices (#30983)
* add devtool for viewing users and their devices * show number of devices * apply changes from review * Fix typo Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
This commit is contained in:
@@ -83,6 +83,11 @@ exports[`DevtoolsDialog renders the devtools dialog 1`] = `
|
||||
>
|
||||
Active Widgets
|
||||
</button>
|
||||
<button
|
||||
class="mx_DevTools_button"
|
||||
>
|
||||
Users
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<h2
|
||||
|
||||
361
test/unit-tests/components/views/dialogs/devtools/Users-test.tsx
Normal file
361
test/unit-tests/components/views/dialogs/devtools/Users-test.tsx
Normal file
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { Device, DeviceVerification, type MatrixClient, MatrixEvent, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
import { render, screen, waitFor } from "jest-matrix-react";
|
||||
import { Room, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
|
||||
import { type DeviceVerificationStatus, type UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import { createTestClient } from "../../../../../test-utils";
|
||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||
import { DevtoolsContext } from "../../../../../../src/components/views/dialogs/devtools/BaseTool";
|
||||
import { UserList } from "../../../../../../src/components/views/dialogs/devtools/Users";
|
||||
|
||||
const userId = "@alice:example.com";
|
||||
|
||||
describe("<Users />", () => {
|
||||
let matrixClient: MatrixClient;
|
||||
beforeEach(() => {
|
||||
matrixClient = createTestClient();
|
||||
});
|
||||
|
||||
it("should render a user list", () => {
|
||||
const room = new Room("!roomId", matrixClient, userId, {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
room.getJoinedMembers = jest.fn().mockReturnValue([]);
|
||||
|
||||
const { asFragment } = render(
|
||||
<MatrixClientContext.Provider value={matrixClient}>
|
||||
<DevtoolsContext.Provider value={{ room }}>
|
||||
<UserList onBack={() => {}} />
|
||||
</DevtoolsContext.Provider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render a single user", async () => {
|
||||
const room = new Room("!roomId", matrixClient, userId, {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
|
||||
const alice = new RoomMember("!roomId", userId);
|
||||
alice.setMembershipEvent(
|
||||
new MatrixEvent({
|
||||
content: {
|
||||
membership: "join",
|
||||
},
|
||||
state_key: userId,
|
||||
room_id: "!roomId",
|
||||
type: "m.room.member",
|
||||
sender: userId,
|
||||
}),
|
||||
);
|
||||
room.getJoinedMembers = jest.fn().mockReturnValue([alice]);
|
||||
|
||||
mocked(matrixClient.getCrypto()!.getUserVerificationStatus).mockResolvedValue({
|
||||
isCrossSigningVerified: jest.fn().mockReturnValue(true),
|
||||
wasCrossSigningVerified: jest.fn().mockReturnValue(true),
|
||||
needsUserApproval: false,
|
||||
} as unknown as UserVerificationStatus);
|
||||
mocked(matrixClient.getCrypto()!.getUserDeviceInfo).mockResolvedValue(
|
||||
new Map([
|
||||
[
|
||||
userId,
|
||||
new Map([
|
||||
[
|
||||
"VERIFIED",
|
||||
new Device({
|
||||
deviceId: "VERIFIED",
|
||||
userId: userId,
|
||||
algorithms: [],
|
||||
keys: new Map([
|
||||
["ed25519:VERIFIED", "an_ed25519_public_key"],
|
||||
["curve25519:VERIFIED", "a_curve25519_public_key"],
|
||||
]),
|
||||
}),
|
||||
],
|
||||
[
|
||||
"SIGNED",
|
||||
new Device({
|
||||
deviceId: "SIGNED",
|
||||
userId: userId,
|
||||
algorithms: [],
|
||||
keys: new Map([
|
||||
["ed25519:SIGNED", "an_ed25519_public_key"],
|
||||
["curve25519:SIGNED", "a_curve25519_public_key"],
|
||||
]),
|
||||
}),
|
||||
],
|
||||
[
|
||||
"UNSIGNED",
|
||||
new Device({
|
||||
deviceId: "UNSIGNED",
|
||||
userId: userId,
|
||||
algorithms: [],
|
||||
keys: new Map([
|
||||
["ed25519:UNSIGNED", "an_ed25519_public_key"],
|
||||
["curve25519:UNSIGNED", "a_curve25519_public_key"],
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
],
|
||||
]),
|
||||
);
|
||||
mocked(matrixClient.getCrypto()!.getDeviceVerificationStatus).mockImplementation(
|
||||
async (userId: string, deviceId: string) => {
|
||||
switch (deviceId) {
|
||||
case "VERIFIED":
|
||||
return {
|
||||
signedByOwner: true,
|
||||
crossSigningVerified: true,
|
||||
} as unknown as DeviceVerificationStatus;
|
||||
case "SIGNED":
|
||||
return {
|
||||
signedByOwner: true,
|
||||
crossSigningVerified: false,
|
||||
} as unknown as DeviceVerificationStatus;
|
||||
case "UNSIGNED":
|
||||
return {
|
||||
signedByOwner: false,
|
||||
crossSigningVerified: false,
|
||||
} as unknown as DeviceVerificationStatus;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const { asFragment } = render(
|
||||
<MatrixClientContext.Provider value={matrixClient}>
|
||||
<DevtoolsContext.Provider value={{ room }}>
|
||||
<UserList onBack={() => {}} />
|
||||
</DevtoolsContext.Provider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
screen.getByRole("button", { name: userId }).click();
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/Verification status:/)).toHaveTextContent(/Verified/));
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "VERIFIED" })).toBeInTheDocument());
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render a single device - verified by cross-signing", async () => {
|
||||
const room = new Room("!roomId", matrixClient, userId, {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
|
||||
const alice = new RoomMember("!roomId", userId);
|
||||
alice.setMembershipEvent(
|
||||
new MatrixEvent({
|
||||
content: {
|
||||
membership: "join",
|
||||
},
|
||||
state_key: userId,
|
||||
room_id: "!roomId",
|
||||
type: "m.room.member",
|
||||
sender: userId,
|
||||
}),
|
||||
);
|
||||
room.getJoinedMembers = jest.fn().mockReturnValue([alice]);
|
||||
|
||||
mocked(matrixClient.getCrypto()!.getUserVerificationStatus).mockResolvedValue({
|
||||
isCrossSigningVerified: jest.fn().mockReturnValue(true),
|
||||
wasCrossSigningVerified: jest.fn().mockReturnValue(true),
|
||||
needsUserApproval: false,
|
||||
} as unknown as UserVerificationStatus);
|
||||
mocked(matrixClient.getCrypto()!.getUserDeviceInfo).mockResolvedValue(
|
||||
new Map([
|
||||
[
|
||||
userId,
|
||||
new Map([
|
||||
[
|
||||
"VERIFIED",
|
||||
new Device({
|
||||
deviceId: "VERIFIED",
|
||||
userId: userId,
|
||||
algorithms: [],
|
||||
keys: new Map([
|
||||
["ed25519:VERIFIED", "an_ed25519_public_key"],
|
||||
["curve25519:VERIFIED", "a_curve25519_public_key"],
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
],
|
||||
]),
|
||||
);
|
||||
mocked(matrixClient.getCrypto()!.getDeviceVerificationStatus).mockResolvedValue({
|
||||
signedByOwner: true,
|
||||
crossSigningVerified: true,
|
||||
} as unknown as DeviceVerificationStatus);
|
||||
|
||||
const { asFragment } = render(
|
||||
<MatrixClientContext.Provider value={matrixClient}>
|
||||
<DevtoolsContext.Provider value={{ room }}>
|
||||
<UserList onBack={() => {}} />
|
||||
</DevtoolsContext.Provider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
screen.getByRole("button", { name: userId }).click();
|
||||
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "VERIFIED" })).toBeInTheDocument());
|
||||
screen.getByRole("button", { name: "VERIFIED" }).click();
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText(/Verification status:/)).toHaveTextContent(/Verified by cross-signing/),
|
||||
);
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render a single device - signed by owner", async () => {
|
||||
const room = new Room("!roomId", matrixClient, userId, {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
|
||||
const alice = new RoomMember("!roomId", userId);
|
||||
alice.setMembershipEvent(
|
||||
new MatrixEvent({
|
||||
content: {
|
||||
membership: "join",
|
||||
},
|
||||
state_key: userId,
|
||||
room_id: "!roomId",
|
||||
type: "m.room.member",
|
||||
sender: userId,
|
||||
}),
|
||||
);
|
||||
room.getJoinedMembers = jest.fn().mockReturnValue([alice]);
|
||||
|
||||
mocked(matrixClient.getCrypto()!.getUserVerificationStatus).mockResolvedValue({
|
||||
isCrossSigningVerified: jest.fn().mockReturnValue(true),
|
||||
wasCrossSigningVerified: jest.fn().mockReturnValue(true),
|
||||
needsUserApproval: false,
|
||||
} as unknown as UserVerificationStatus);
|
||||
mocked(matrixClient.getCrypto()!.getUserDeviceInfo).mockResolvedValue(
|
||||
new Map([
|
||||
[
|
||||
userId,
|
||||
new Map([
|
||||
[
|
||||
"SIGNED",
|
||||
new Device({
|
||||
deviceId: "SIGNED",
|
||||
userId: userId,
|
||||
algorithms: [],
|
||||
keys: new Map([
|
||||
["ed25519:SIGNED", "an_ed25519_public_key"],
|
||||
["curve25519:SIGNED", "a_curve25519_public_key"],
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
],
|
||||
]),
|
||||
);
|
||||
mocked(matrixClient.getCrypto()!.getDeviceVerificationStatus).mockResolvedValue({
|
||||
signedByOwner: true,
|
||||
crossSigningVerified: false,
|
||||
} as unknown as DeviceVerificationStatus);
|
||||
|
||||
const { asFragment } = render(
|
||||
<MatrixClientContext.Provider value={matrixClient}>
|
||||
<DevtoolsContext.Provider value={{ room }}>
|
||||
<UserList onBack={() => {}} />
|
||||
</DevtoolsContext.Provider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
screen.getByRole("button", { name: userId }).click();
|
||||
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "SIGNED" })).toBeInTheDocument());
|
||||
screen.getByRole("button", { name: "SIGNED" }).click();
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/Verification status:/)).toHaveTextContent(/Signed by owner/));
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render a single device - unsigned", async () => {
|
||||
const room = new Room("!roomId", matrixClient, userId, {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
|
||||
const alice = new RoomMember("!roomId", userId);
|
||||
alice.setMembershipEvent(
|
||||
new MatrixEvent({
|
||||
content: {
|
||||
membership: "join",
|
||||
},
|
||||
state_key: userId,
|
||||
room_id: "!roomId",
|
||||
type: "m.room.member",
|
||||
sender: userId,
|
||||
}),
|
||||
);
|
||||
room.getJoinedMembers = jest.fn().mockReturnValue([alice]);
|
||||
|
||||
mocked(matrixClient.getCrypto()!.getUserVerificationStatus).mockResolvedValue({
|
||||
isCrossSigningVerified: jest.fn().mockReturnValue(true),
|
||||
wasCrossSigningVerified: jest.fn().mockReturnValue(true),
|
||||
needsUserApproval: false,
|
||||
} as unknown as UserVerificationStatus);
|
||||
mocked(matrixClient.getCrypto()!.getUserDeviceInfo).mockResolvedValue(
|
||||
new Map([
|
||||
[
|
||||
userId,
|
||||
new Map([
|
||||
[
|
||||
"UNSIGNED",
|
||||
new Device({
|
||||
deviceId: "UNSIGNED",
|
||||
userId: userId,
|
||||
algorithms: [],
|
||||
keys: new Map([
|
||||
["ed25519:UNSIGNED", "an_ed25519_public_key"],
|
||||
["curve25519:UNSIGNED", "a_curve25519_public_key"],
|
||||
]),
|
||||
verified: DeviceVerification.Verified,
|
||||
}),
|
||||
],
|
||||
]),
|
||||
],
|
||||
]),
|
||||
);
|
||||
mocked(matrixClient.getCrypto()!.getDeviceVerificationStatus).mockResolvedValue({
|
||||
signedByOwner: false,
|
||||
crossSigningVerified: false,
|
||||
} as unknown as DeviceVerificationStatus);
|
||||
|
||||
const { asFragment } = render(
|
||||
<MatrixClientContext.Provider value={matrixClient}>
|
||||
<DevtoolsContext.Provider value={{ room }}>
|
||||
<UserList onBack={() => {}} />
|
||||
</DevtoolsContext.Provider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
screen.getByRole("button", { name: userId }).click();
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/Verification status:/)).toHaveTextContent(/Verified/));
|
||||
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "UNSIGNED" })).toBeInTheDocument());
|
||||
screen.getByRole("button", { name: "UNSIGNED" }).click();
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/Verification status:/)).toHaveTextContent(/Not signed by owner/));
|
||||
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,454 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Users /> should render a single device - signed by owner 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_DevTools_content"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
User ID: @alice:example.com
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
Device ID: SIGNED
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
Displayname:
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
Verification status:
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_normal mx_E2EIcon_inline"
|
||||
data-testid="e2e-icon"
|
||||
/>
|
||||
Signed by owner
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
Dehydrated: No
|
||||
</li>
|
||||
<li>
|
||||
Device keys
|
||||
<ul>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
ed25519: an_ed25519_public_key
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
curve25519: a_curve25519_public_key
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<button>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<Users /> should render a single device - unsigned 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_DevTools_content"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
User ID: @alice:example.com
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
Device ID: UNSIGNED
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
Displayname:
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
Verification status:
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_warning mx_E2EIcon_inline"
|
||||
data-testid="e2e-icon"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon_normal"
|
||||
/>
|
||||
</div>
|
||||
Not signed by owner
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
Dehydrated: No
|
||||
</li>
|
||||
<li>
|
||||
Device keys
|
||||
<ul>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
ed25519: an_ed25519_public_key
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
curve25519: a_curve25519_public_key
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<button>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<Users /> should render a single device - verified by cross-signing 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_DevTools_content"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
User ID: @alice:example.com
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
Device ID: VERIFIED
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
Displayname:
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
Verification status:
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_verified mx_E2EIcon_inline"
|
||||
data-testid="e2e-icon"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon_normal"
|
||||
/>
|
||||
</div>
|
||||
Verified by cross-signing
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
Dehydrated: No
|
||||
</li>
|
||||
<li>
|
||||
Device keys
|
||||
<ul>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
ed25519: an_ed25519_public_key
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
curve25519: a_curve25519_public_key
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<button>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<Users /> should render a single user 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_DevTools_content"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<div
|
||||
class="mx_CopyableText"
|
||||
>
|
||||
User ID: @alice:example.com
|
||||
<div
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
Membership: join
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
Displayname:
|
||||
<i>
|
||||
None
|
||||
</i>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
Avatar:
|
||||
<i>
|
||||
None
|
||||
</i>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>
|
||||
Verification status:
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_verified mx_E2EIcon_inline"
|
||||
data-testid="e2e-icon"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon_normal"
|
||||
/>
|
||||
</div>
|
||||
Verified
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<section>
|
||||
<h2>
|
||||
Cryptographic devices (3)
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<button
|
||||
class="mx_DevTools_button"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_verified mx_E2EIcon_inline"
|
||||
data-testid="e2e-icon"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon_normal"
|
||||
/>
|
||||
</div>
|
||||
VERIFIED
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
class="mx_DevTools_button"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_normal mx_E2EIcon_inline"
|
||||
data-testid="e2e-icon"
|
||||
/>
|
||||
SIGNED
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
class="mx_DevTools_button"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_warning mx_E2EIcon_inline"
|
||||
data-testid="e2e-icon"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon_normal"
|
||||
/>
|
||||
</div>
|
||||
UNSIGNED
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<button>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<Users /> should render a user list 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_DevTools_content"
|
||||
>
|
||||
<div
|
||||
class="mx_Field mx_Field_input mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
id="mx_Field_1"
|
||||
label="Filter results"
|
||||
placeholder="Filter results"
|
||||
size="64"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_Field_1"
|
||||
>
|
||||
Filter results
|
||||
</label>
|
||||
</div>
|
||||
No results found
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_label"
|
||||
>
|
||||
<div
|
||||
id="mx_LabelledToggleSwitch__r_4_"
|
||||
>
|
||||
Only joined users
|
||||
</div>
|
||||
</span>
|
||||
<div
|
||||
aria-checked="true"
|
||||
aria-disabled="false"
|
||||
aria-labelledby="mx_LabelledToggleSwitch__r_4_"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on mx_ToggleSwitch_enabled"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<button>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
Reference in New Issue
Block a user