mv element.io @types __mocks__/ debian docker module_system/ playwright res src test webapp Dockerfile .dockerignore .eslintignore .stylelintrc.cjs babel.config.cjs recorder-worklet-loader.cjs .modernizr.json components.json config.json config.sample.json package.json project.json tsconfig.json tsconfig.module_system.json jest.config.ts playwright.config.ts webpack.config.ts build_config.sample.yaml apps/web/

mkdir apps/web/scripts
mv scripts/{cleanup.sh,ci_package.sh,copy-res.ts,deploy.py,package.sh} apps/web/scripts

And a couple of gitignore tweaks

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2026-02-24 15:43:58 +00:00
parent e7509c92a1
commit 91a3cb03c1
3408 changed files with 28 additions and 32 deletions
@@ -0,0 +1,94 @@
/*
* 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 { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { render, screen, waitFor } from "jest-matrix-react";
import { type KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils";
import { Crypto } from "../../../../../../src/components/views/dialogs/devtools/Crypto";
describe("<Crypto />", () => {
let matrixClient: MatrixClient;
beforeEach(() => {
matrixClient = createTestClient();
});
function renderComponent() {
return render(<Crypto onBack={jest.fn} />, withClientContextRenderOptions(matrixClient));
}
it("should display message if crypto is not available", async () => {
jest.spyOn(matrixClient, "getCrypto").mockReturnValue(undefined);
renderComponent();
expect(screen.getByText("Cryptographic module is not available")).toBeInTheDocument();
});
describe("<KeyStorage />", () => {
it("should display loading spinner while loading", async () => {
jest.spyOn(matrixClient.getCrypto()!, "getKeyBackupInfo").mockImplementation(() => new Promise(() => {}));
renderComponent();
await waitFor(() => expect(screen.getByLabelText("Loading…")).toBeInTheDocument());
});
it("should display when the key storage data are missing", async () => {
renderComponent();
await waitFor(() => expect(screen.getByRole("table", { name: "Key Storage" })).toBeInTheDocument());
expect(screen.getByRole("table", { name: "Key Storage" })).toMatchSnapshot();
});
it("should display when the key storage data are available", async () => {
jest.spyOn(matrixClient.getCrypto()!, "getKeyBackupInfo").mockResolvedValue({
algorithm: "m.megolm_backup.v1",
version: "1",
} as unknown as KeyBackupInfo);
jest.spyOn(matrixClient, "isKeyBackupKeyStored").mockResolvedValue({});
jest.spyOn(matrixClient.getCrypto()!, "getSessionBackupPrivateKey").mockResolvedValue(new Uint8Array(32));
jest.spyOn(matrixClient.getCrypto()!, "getActiveSessionBackupVersion").mockResolvedValue("2");
jest.spyOn(matrixClient.secretStorage, "hasKey").mockResolvedValue(true);
jest.spyOn(matrixClient.getCrypto()!, "isSecretStorageReady").mockResolvedValue(true);
renderComponent();
await waitFor(() => expect(screen.getByRole("table", { name: "Key Storage" })).toBeInTheDocument());
expect(screen.getByRole("table", { name: "Key Storage" })).toMatchSnapshot();
});
});
describe("<CrossSigning />", () => {
it("should display loading spinner while loading", async () => {
jest.spyOn(matrixClient.getCrypto()!, "getCrossSigningStatus").mockImplementation(
() => new Promise(() => {}),
);
renderComponent();
await waitFor(() => expect(screen.getByLabelText("Loading…")).toBeInTheDocument());
});
it("should display when the cross-signing data are missing", async () => {
renderComponent();
await waitFor(() => expect(screen.getByRole("table", { name: "Cross-signing" })).toBeInTheDocument());
expect(screen.getByRole("table", { name: "Cross-signing" })).toMatchSnapshot();
});
it("should display when the cross-signing data are available", async () => {
jest.spyOn(matrixClient.getCrypto()!, "getCrossSigningStatus").mockResolvedValue({
publicKeysOnDevice: true,
privateKeysInSecretStorage: true,
privateKeysCachedLocally: {
masterKey: true,
selfSigningKey: true,
userSigningKey: true,
},
});
jest.spyOn(matrixClient.getCrypto()!, "isCrossSigningReady").mockResolvedValue(true);
renderComponent();
await waitFor(() => expect(screen.getByRole("table", { name: "Cross-signing" })).toBeInTheDocument());
expect(screen.getByRole("table", { name: "Cross-signing" })).toMatchSnapshot();
});
});
});
@@ -0,0 +1,62 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render } from "jest-matrix-react";
import { Room, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
import { stubClient } from "../../../../../test-utils";
import { DevtoolsContext } from "../../../../../../src/components/views/dialogs/devtools/BaseTool";
import { TimelineEventEditor } from "../../../../../../src/components/views/dialogs/devtools/Event";
describe("<EventEditor />", () => {
beforeEach(() => {
stubClient();
});
it("should render", () => {
const cli = MatrixClientPeg.safeGet();
const { asFragment } = render(
<MatrixClientContext.Provider value={cli}>
<DevtoolsContext.Provider
value={{
room: new Room("!roomId", cli, "@alice:example.com", {
pendingEventOrdering: PendingEventOrdering.Detached,
}),
}}
>
<TimelineEventEditor onBack={() => {}} />
</DevtoolsContext.Provider>
</MatrixClientContext.Provider>,
);
expect(asFragment()).toMatchSnapshot();
});
describe("thread context", () => {
it("should pre-populate a thread relationship", () => {
const cli = MatrixClientPeg.safeGet();
const { asFragment } = render(
<MatrixClientContext.Provider value={cli}>
<DevtoolsContext.Provider
value={{
room: new Room("!roomId", cli, "@alice:example.com", {
pendingEventOrdering: PendingEventOrdering.Detached,
}),
threadRootId: "$this_is_a_thread_id",
}}
>
<TimelineEventEditor onBack={() => {}} />
</DevtoolsContext.Provider>
</MatrixClientContext.Provider>,
);
expect(asFragment()).toMatchSnapshot();
});
});
});
@@ -0,0 +1,41 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render } from "jest-matrix-react";
import { Room, PendingEventOrdering } from "matrix-js-sdk/src/matrix";
import RoomNotifications from "../../../../../../src/components/views/dialogs/devtools/RoomNotifications";
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
import { stubClient } from "../../../../../test-utils";
import { DevtoolsContext } from "../../../../../../src/components/views/dialogs/devtools/BaseTool";
describe("<RoomNotifications />", () => {
beforeEach(() => {
stubClient();
});
it("should render", () => {
const cli = MatrixClientPeg.safeGet();
const { asFragment } = render(
<MatrixClientContext.Provider value={cli}>
<DevtoolsContext.Provider
value={{
room: new Room("!roomId", cli, "@alice:example.com", {
pendingEventOrdering: PendingEventOrdering.Detached,
}),
}}
>
<RoomNotifications onBack={() => {}} setTool={() => {}} />
</DevtoolsContext.Provider>
</MatrixClientContext.Provider>,
);
expect(asFragment()).toMatchSnapshot();
});
});
@@ -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,289 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<Crypto /> <CrossSigning /> should display when the cross-signing data are available 1`] = `
<table
aria-label="Cross-signing"
>
<thead>
Cross-signing
</thead>
<tbody>
<tr>
<th
scope="row"
>
Cross-signing status:
</th>
<td>
Cross-signing is ready for use.
</td>
</tr>
<tr>
<th
scope="row"
>
Cross-signing public keys:
</th>
<td>
in memory
</td>
</tr>
<tr>
<th
scope="row"
>
Cross-signing private keys:
</th>
<td>
in secret storage
</td>
</tr>
<tr>
<th
scope="row"
>
Master private key:
</th>
<td>
cached locally
</td>
</tr>
<tr>
<th
scope="row"
>
Self signing private key:
</th>
<td>
cached locally
</td>
</tr>
<tr>
<th
scope="row"
>
User signing private key:
</th>
<td>
cached locally
</td>
</tr>
</tbody>
</table>
`;
exports[`<Crypto /> <CrossSigning /> should display when the cross-signing data are missing 1`] = `
<table
aria-label="Cross-signing"
>
<thead>
Cross-signing
</thead>
<tbody>
<tr>
<th
scope="row"
>
Cross-signing status:
</th>
<td>
Cross-signing is not set up.
</td>
</tr>
<tr>
<th
scope="row"
>
Cross-signing public keys:
</th>
<td>
not found
</td>
</tr>
<tr>
<th
scope="row"
>
Cross-signing private keys:
</th>
<td>
not found in storage
</td>
</tr>
<tr>
<th
scope="row"
>
Master private key:
</th>
<td>
not found locally
</td>
</tr>
<tr>
<th
scope="row"
>
Self signing private key:
</th>
<td>
not found locally
</td>
</tr>
<tr>
<th
scope="row"
>
User signing private key:
</th>
<td>
not found locally
</td>
</tr>
</tbody>
</table>
`;
exports[`<Crypto /> <KeyStorage /> should display when the key storage data are available 1`] = `
<table
aria-label="Key Storage"
>
<thead>
Key Storage
</thead>
<tbody>
<tr>
<th
scope="row"
>
Latest backup version on server:
</th>
<td>
1 (Algorithm: m.megolm_backup.v1)
</td>
</tr>
<tr>
<th
scope="row"
>
Backup key stored:
</th>
<td>
in secret storage
</td>
</tr>
<tr>
<th
scope="row"
>
Active backup version:
</th>
<td>
2
</td>
</tr>
<tr>
<th
scope="row"
>
Backup key cached:
</th>
<td>
cached locally, well formed
</td>
</tr>
<tr>
<th
scope="row"
>
Secret storage public key:
</th>
<td>
in account data
</td>
</tr>
<tr>
<th
scope="row"
>
Secret storage:
</th>
<td>
ready
</td>
</tr>
</tbody>
</table>
`;
exports[`<Crypto /> <KeyStorage /> should display when the key storage data are missing 1`] = `
<table
aria-label="Key Storage"
>
<thead>
Key Storage
</thead>
<tbody>
<tr>
<th
scope="row"
>
Latest backup version on server:
</th>
<td>
Your keys are not being backed up from this session.
</td>
</tr>
<tr>
<th
scope="row"
>
Backup key stored:
</th>
<td>
not stored
</td>
</tr>
<tr>
<th
scope="row"
>
Active backup version:
</th>
<td>
None
</td>
</tr>
<tr>
<th
scope="row"
>
Backup key cached:
</th>
<td>
not found locally, unexpected type
</td>
</tr>
<tr>
<th
scope="row"
>
Secret storage public key:
</th>
<td>
not found
</td>
</tr>
<tr>
<th
scope="row"
>
Secret storage:
</th>
<td>
not ready
</td>
</tr>
</tbody>
</table>
`;
@@ -0,0 +1,126 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<EventEditor /> should render 1`] = `
<DocumentFragment>
<div
class="mx_DevTools_content"
>
<div
class="mx_DevTools_eventTypeStateKeyGroup"
>
<div
class="mx_Field mx_Field_input"
>
<input
autocomplete="on"
id="eventType"
label="Event Type"
placeholder="Event Type"
size="42"
type="text"
value=""
/>
<label
for="eventType"
>
Event Type
</label>
</div>
</div>
<div
class="mx_Field mx_Field_textarea mx_DevTools_textarea"
>
<textarea
autocomplete="off"
id="evContent"
label="Event Content"
placeholder="Event Content"
type="text"
>
{
}
</textarea>
<label
for="evContent"
>
Event Content
</label>
</div>
</div>
<div
class="mx_Dialog_buttons"
>
<button>
Back
</button>
<button>
Send
</button>
</div>
</DocumentFragment>
`;
exports[`<EventEditor /> thread context should pre-populate a thread relationship 1`] = `
<DocumentFragment>
<div
class="mx_DevTools_content"
>
<div
class="mx_DevTools_eventTypeStateKeyGroup"
>
<div
class="mx_Field mx_Field_input"
>
<input
autocomplete="on"
id="eventType"
label="Event Type"
placeholder="Event Type"
size="42"
type="text"
value=""
/>
<label
for="eventType"
>
Event Type
</label>
</div>
</div>
<div
class="mx_Field mx_Field_textarea mx_DevTools_textarea"
>
<textarea
autocomplete="off"
id="evContent"
label="Event Content"
placeholder="Event Content"
type="text"
>
{
"m.relates_to": {
"rel_type": "m.thread",
"event_id": "$this_is_a_thread_id"
}
}
</textarea>
<label
for="evContent"
>
Event Content
</label>
</div>
</div>
<div
class="mx_Dialog_buttons"
>
<button>
Back
</button>
<button>
Send
</button>
</div>
</DocumentFragment>
`;
@@ -0,0 +1,72 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<RoomNotifications /> should render 1`] = `
<DocumentFragment>
<div
class="mx_DevTools_content"
>
<section>
<h2>
Room status
</h2>
<ul>
<li>
<span>
Room unread status:
<strong>
None
</strong>
, count:
<strong>
0
</strong>
</span>
</li>
<li>
<span>
Notification state is
<strong />
</span>
</li>
<li>
<span>
Room is
<strong>
not encrypted 🚨
</strong>
</span>
</li>
</ul>
</section>
<section>
<h2>
Main timeline
</h2>
<ul>
<li>
Total: 0
</li>
<li>
Highlight: 0
</li>
<li>
Dot: false
</li>
</ul>
</section>
<section>
<h2>
Threads timeline
</h2>
<ul />
</section>
</div>
<div
class="mx_Dialog_buttons"
>
<button>
Back
</button>
</div>
</DocumentFragment>
`;
@@ -0,0 +1,737 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</div>
</li>
<li>
<div
class="mx_CopyableText"
>
Device ID: SIGNED
<div
aria-label="Copy"
class="mx_AccessibleButton mx_CopyableText_copyButton"
role="button"
tabindex="0"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</div>
</li>
<li>
Displayname:
</li>
<li>
<span>
Verification status:
<div
class="mx_E2EIcon mx_E2EIcon_inline"
data-testid="e2e-icon"
>
<svg
color="var(--cpd-color-icon-tertiary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
/>
</svg>
</div>
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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</div>
</li>
<li>
<div
class="mx_CopyableText"
>
Device ID: UNSIGNED
<div
aria-label="Copy"
class="mx_AccessibleButton mx_CopyableText_copyButton"
role="button"
tabindex="0"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</div>
</li>
<li>
Displayname:
</li>
<li>
<span>
Verification status:
<div
class="mx_E2EIcon mx_E2EIcon_inline"
data-testid="e2e-icon"
>
<svg
color="var(--cpd-color-icon-critical-primary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
</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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</div>
</li>
<li>
<div
class="mx_CopyableText"
>
Device ID: VERIFIED
<div
aria-label="Copy"
class="mx_AccessibleButton mx_CopyableText_copyButton"
role="button"
tabindex="0"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</div>
</li>
<li>
Displayname:
</li>
<li>
<span>
Verification status:
<div
class="mx_E2EIcon mx_E2EIcon_inline"
data-testid="e2e-icon"
>
<svg
color="var(--cpd-color-icon-success-primary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M11.106 2.447a2 2 0 0 1 1.789 0l6 3A2 2 0 0 1 20 7.237V12c0 6.742-5.773 9.246-7.51 9.846-.32.111-.66.111-.98 0C9.774 21.246 4 18.743 4 12V7.236a2 2 0 0 1 1.105-1.789zm4.601 6.846a1 1 0 0 0-1.414 0L11 12.586l-1.293-1.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0 0-1.414"
fill-rule="evenodd"
/>
</svg>
</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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</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"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</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_inline"
data-testid="e2e-icon"
>
<svg
color="var(--cpd-color-icon-success-primary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M11.106 2.447a2 2 0 0 1 1.789 0l6 3A2 2 0 0 1 20 7.237V12c0 6.742-5.773 9.246-7.51 9.846-.32.111-.66.111-.98 0C9.774 21.246 4 18.743 4 12V7.236a2 2 0 0 1 1.105-1.789zm4.601 6.846a1 1 0 0 0-1.414 0L11 12.586l-1.293-1.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0 0-1.414"
fill-rule="evenodd"
/>
</svg>
</div>
Verified
</span>
</li>
</ul>
<section>
<h2>
Cryptographic devices (3)
</h2>
<ul>
<li>
<button
class="mx_DevTools_button"
>
<div
class="mx_E2EIcon mx_E2EIcon_inline"
data-testid="e2e-icon"
>
<svg
color="var(--cpd-color-icon-success-primary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M11.106 2.447a2 2 0 0 1 1.789 0l6 3A2 2 0 0 1 20 7.237V12c0 6.742-5.773 9.246-7.51 9.846-.32.111-.66.111-.98 0C9.774 21.246 4 18.743 4 12V7.236a2 2 0 0 1 1.105-1.789zm4.601 6.846a1 1 0 0 0-1.414 0L11 12.586l-1.293-1.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0 0-1.414"
fill-rule="evenodd"
/>
</svg>
</div>
VERIFIED
</button>
</li>
<li>
<button
class="mx_DevTools_button"
>
<div
class="mx_E2EIcon mx_E2EIcon_inline"
data-testid="e2e-icon"
>
<svg
color="var(--cpd-color-icon-tertiary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 22q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 20V10q0-.825.588-1.412A1.93 1.93 0 0 1 6 8h1V6q0-2.075 1.463-3.537Q9.926 1 12 1q2.075 0 3.537 1.463Q17 3.925 17 6v2h1q.824 0 1.413.588Q20 9.175 20 10v10q0 .824-.587 1.413A1.93 1.93 0 0 1 18 22zM9 8h6V6q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 3q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 6z"
/>
</svg>
</div>
SIGNED
</button>
</li>
<li>
<button
class="mx_DevTools_button"
>
<div
class="mx_E2EIcon mx_E2EIcon_inline"
data-testid="e2e-icon"
>
<svg
color="var(--cpd-color-icon-critical-primary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
</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
<form
class="_root_19upo_16"
>
<div
class="_inline-field_19upo_32"
>
<div
class="_inline-field-control_19upo_44"
>
<div
class="_container_udcm8_10"
>
<input
checked=""
class="_input_udcm8_24"
id="_r_4_"
role="switch"
type="checkbox"
/>
<div
class="_ui_udcm8_34"
/>
</div>
</div>
<div
class="_inline-field-body_19upo_38"
>
<label
class="_label_19upo_59"
for="_r_4_"
>
Only joined users
</label>
</div>
</div>
</form>
</div>
<div
class="mx_Dialog_buttons"
>
<button>
Back
</button>
</div>
</DocumentFragment>
`;