Merge branch 'develop' into midhun/fix-spotlight-1
This commit is contained in:
commit
ec96d33ed7
215 changed files with 3574 additions and 2704 deletions
|
@ -351,7 +351,7 @@ describe("Notifier", () => {
|
|||
user: mockClient.getSafeUserId(),
|
||||
room: testRoom.roomId,
|
||||
});
|
||||
addReplyToMessageContent(reply.getContent(), event, { includeLegacyFallback: true });
|
||||
addReplyToMessageContent(reply.getContent(), event);
|
||||
Notifier.displayPopupNotification(reply, testRoom);
|
||||
expect(MockPlatform.displayNotification).toHaveBeenCalledWith(
|
||||
"@bob:example.org (!room1:server)",
|
||||
|
|
|
@ -6,42 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import {
|
||||
IContent,
|
||||
MatrixEvent,
|
||||
MsgType,
|
||||
M_BEACON_INFO,
|
||||
LocationAssetType,
|
||||
M_ASSET,
|
||||
M_POLL_END,
|
||||
Room,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import {
|
||||
getNestedReplyText,
|
||||
getParentEventId,
|
||||
shouldDisplayReply,
|
||||
stripHTMLReply,
|
||||
stripPlainReply,
|
||||
} from "../../src/utils/Reply";
|
||||
import { makePollStartEvent, mkEvent, stubClient } from "../test-utils";
|
||||
import { RoomPermalinkCreator } from "../../src/utils/permalinks/Permalinks";
|
||||
|
||||
function makeTestEvent(type: string, content: IContent): MatrixEvent {
|
||||
return mkEvent({
|
||||
event: true,
|
||||
type: type,
|
||||
user: "@user1:server",
|
||||
room: "!room1:server",
|
||||
content,
|
||||
});
|
||||
}
|
||||
|
||||
const mockPermalinkGenerator = {
|
||||
forEvent(eventId: string): string {
|
||||
return "$$permalink$$";
|
||||
},
|
||||
} as RoomPermalinkCreator;
|
||||
import { getParentEventId, shouldDisplayReply, stripHTMLReply, stripPlainReply } from "../../src/utils/Reply";
|
||||
import { mkEvent, stubClient } from "../test-utils";
|
||||
|
||||
// don't litter test console with logs
|
||||
jest.mock("matrix-js-sdk/src/logger");
|
||||
|
@ -122,50 +90,6 @@ But this is not
|
|||
});
|
||||
});
|
||||
|
||||
describe("getNestedReplyText", () => {
|
||||
it("Returns valid reply fallback text for m.text msgtypes", () => {
|
||||
const event = makeTestEvent(MsgType.Text, {
|
||||
body: "body",
|
||||
msgtype: "m.text",
|
||||
});
|
||||
|
||||
expect(getNestedReplyText(event, mockPermalinkGenerator)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
(
|
||||
[
|
||||
["m.room.message", MsgType.Location, LocationAssetType.Pin],
|
||||
["m.room.message", MsgType.Location, LocationAssetType.Self],
|
||||
[M_BEACON_INFO.name, undefined, LocationAssetType.Pin],
|
||||
[M_BEACON_INFO.name, undefined, LocationAssetType.Self],
|
||||
] as const
|
||||
).forEach(([type, msgType, assetType]) => {
|
||||
it(`should create the expected fallback text for ${assetType} ${type}/${msgType}`, () => {
|
||||
const event = makeTestEvent(type, {
|
||||
body: "body",
|
||||
msgtype: msgType,
|
||||
[M_ASSET.name]: { type: assetType },
|
||||
});
|
||||
|
||||
expect(getNestedReplyText(event, mockPermalinkGenerator)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it("should create the expected fallback text for poll end events", () => {
|
||||
const event = makeTestEvent(M_POLL_END.name, {
|
||||
body: "body",
|
||||
});
|
||||
|
||||
expect(getNestedReplyText(event, mockPermalinkGenerator)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should create the expected fallback text for poll start events", () => {
|
||||
const event = makePollStartEvent("Will this test pass?", "@user:server.org");
|
||||
|
||||
expect(getNestedReplyText(event, mockPermalinkGenerator)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("shouldDisplayReply", () => {
|
||||
it("Returns false for redacted events", () => {
|
||||
const event = mkEvent({
|
||||
|
|
|
@ -11,6 +11,13 @@ import { CryptoApi } from "matrix-js-sdk/src/crypto-api";
|
|||
|
||||
import { accessSecretStorage } from "../../src/SecurityManager";
|
||||
import { filterConsole, stubClient } from "../test-utils";
|
||||
import Modal from "../../src/Modal.tsx";
|
||||
|
||||
jest.mock("react", () => {
|
||||
const React = jest.requireActual("react");
|
||||
React.lazy = (children: any) => children(); // stub out lazy for dialog test
|
||||
return React;
|
||||
});
|
||||
|
||||
describe("SecurityManager", () => {
|
||||
describe("accessSecretStorage", () => {
|
||||
|
@ -50,5 +57,21 @@ describe("SecurityManager", () => {
|
|||
}).rejects.toThrow("End-to-end encryption is disabled - unable to access secret storage");
|
||||
});
|
||||
});
|
||||
|
||||
it("should show CreateSecretStorageDialog if forceReset=true", async () => {
|
||||
jest.mock("../../src/async-components/views/dialogs/security/CreateSecretStorageDialog", () => ({
|
||||
__test: true,
|
||||
__esModule: true,
|
||||
default: () => jest.fn(),
|
||||
}));
|
||||
const spy = jest.spyOn(Modal, "createDialog");
|
||||
stubClient();
|
||||
|
||||
const func = jest.fn();
|
||||
accessSecretStorage(func, true);
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
await expect(spy.mock.lastCall![0]).resolves.toEqual(expect.objectContaining({ __test: true }));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -62,18 +62,18 @@ describe("SupportedBrowser", () => {
|
|||
);
|
||||
|
||||
it.each([
|
||||
// Safari 17.5 on macOS Sonoma
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
|
||||
// Firefox 129 on macOS Sonoma
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:129.0) Gecko/20100101 Firefox/129.0",
|
||||
// Safari 18.0 on macOS Sonoma
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15",
|
||||
// Firefox 131 on macOS Sonoma
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0",
|
||||
// Edge 129 on Windows
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/129.0.2792.79",
|
||||
// Edge 129 on macOS
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/129.0.2792.79",
|
||||
// Firefox 129 on Windows
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0",
|
||||
// Firefox 129 on Linux
|
||||
"Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:129.0) Gecko/20100101 Firefox/129.0",
|
||||
// Firefox 131 on Windows
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0",
|
||||
// Firefox 131 on Linux
|
||||
"Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0",
|
||||
// Chrome 130 on Windows
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
|
||||
])("should not warn for supported browsers", testUserAgentFactory());
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Reply getNestedReplyText Returns valid reply fallback text for m.text msgtypes 1`] = `
|
||||
{
|
||||
"body": "> <@user1:server> body
|
||||
|
||||
",
|
||||
"html": "<mx-reply><blockquote><a href="$$permalink$$">In reply to</a> <a href="https://matrix.to/#/@user1:server">@user1:server</a><br>body</blockquote></mx-reply>",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Reply getNestedReplyText should create the expected fallback text for m.pin m.room.message/m.location 1`] = `
|
||||
{
|
||||
"body": "> <@user1:server> shared a location.
|
||||
|
||||
",
|
||||
"html": "<mx-reply><blockquote><a href="$$permalink$$">In reply to</a> <a href="https://matrix.to/#/@user1:server">@user1:server</a><br>shared a location.</blockquote></mx-reply>",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Reply getNestedReplyText should create the expected fallback text for m.pin org.matrix.msc3672.beacon_info/undefined 1`] = `
|
||||
{
|
||||
"body": "> <@user1:server> shared a live location.
|
||||
|
||||
",
|
||||
"html": "<mx-reply><blockquote><a href="$$permalink$$">In reply to</a> <a href="https://matrix.to/#/@user1:server">@user1:server</a><br>shared a live location.</blockquote></mx-reply>",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Reply getNestedReplyText should create the expected fallback text for m.self m.room.message/m.location 1`] = `
|
||||
{
|
||||
"body": "> <@user1:server> shared their location.
|
||||
|
||||
",
|
||||
"html": "<mx-reply><blockquote><a href="$$permalink$$">In reply to</a> <a href="https://matrix.to/#/@user1:server">@user1:server</a><br>shared their location.</blockquote></mx-reply>",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Reply getNestedReplyText should create the expected fallback text for m.self org.matrix.msc3672.beacon_info/undefined 1`] = `
|
||||
{
|
||||
"body": "> <@user1:server> shared their live location.
|
||||
|
||||
",
|
||||
"html": "<mx-reply><blockquote><a href="$$permalink$$">In reply to</a> <a href="https://matrix.to/#/@user1:server">@user1:server</a><br>shared their live location.</blockquote></mx-reply>",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Reply getNestedReplyText should create the expected fallback text for poll end events 1`] = `
|
||||
{
|
||||
"body": "> <@user1:server>Ended poll
|
||||
|
||||
",
|
||||
"html": "<mx-reply><blockquote><a href="$$permalink$$">In reply to</a> <a href="https://matrix.to/#/@user1:server">@user1:server</a><br>Ended poll</blockquote></mx-reply>",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Reply getNestedReplyText should create the expected fallback text for poll start events 1`] = `
|
||||
{
|
||||
"body": "> <@user:server.org> started poll: Will this test pass?
|
||||
|
||||
",
|
||||
"html": "<mx-reply><blockquote><a href="$$permalink$$">In reply to</a> <a href="https://matrix.to/#/@user:server.org">@user:server.org</a><br>Poll: Will this test pass?</blockquote></mx-reply>",
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 { render, screen, fireEvent, waitFor } from "jest-matrix-react";
|
||||
|
||||
import RecoveryMethodRemovedDialog from "../../../../../src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog";
|
||||
import Modal from "../../../../../src/Modal.tsx";
|
||||
|
||||
describe("<RecoveryMethodRemovedDialog />", () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should open CreateKeyBackupDialog on primary action click", async () => {
|
||||
const onFinished = jest.fn();
|
||||
const spy = jest.spyOn(Modal, "createDialog");
|
||||
jest.mock("../../../../../src/async-components/views/dialogs/security/CreateKeyBackupDialog", () => ({
|
||||
__test: true,
|
||||
__esModule: true,
|
||||
default: () => <span>mocked dialog</span>,
|
||||
}));
|
||||
|
||||
render(<RecoveryMethodRemovedDialog onFinished={onFinished} />);
|
||||
fireEvent.click(screen.getByRole("button", { name: "Set up Secure Messages" }));
|
||||
await waitFor(() => expect(spy).toHaveBeenCalledTimes(1));
|
||||
expect((spy.mock.lastCall![0] as any)._payload._result).toEqual(expect.objectContaining({ __test: true }));
|
||||
});
|
||||
});
|
|
@ -149,6 +149,7 @@ describe("<MatrixChat />", () => {
|
|||
isRoomEncrypted: jest.fn(),
|
||||
logout: jest.fn(),
|
||||
getDeviceId: jest.fn(),
|
||||
getKeyBackupVersion: jest.fn().mockResolvedValue(null),
|
||||
});
|
||||
let mockClient: Mocked<MatrixClient>;
|
||||
const serverConfig = {
|
||||
|
@ -1515,7 +1516,7 @@ describe("<MatrixChat />", () => {
|
|||
|
||||
describe("when key backup failed", () => {
|
||||
it("should show the new recovery method dialog", async () => {
|
||||
const spy = jest.spyOn(Modal, "createDialogAsync");
|
||||
const spy = jest.spyOn(Modal, "createDialog");
|
||||
jest.mock("../../../../src/async-components/views/dialogs/security/NewRecoveryMethodDialog", () => ({
|
||||
__test: true,
|
||||
__esModule: true,
|
||||
|
@ -1530,7 +1531,25 @@ describe("<MatrixChat />", () => {
|
|||
await flushPromises();
|
||||
mockClient.emit(CryptoEvent.KeyBackupFailed, "error code");
|
||||
await waitFor(() => expect(spy).toHaveBeenCalledTimes(1));
|
||||
expect(await spy.mock.lastCall![0]).toEqual(expect.objectContaining({ __test: true }));
|
||||
expect((spy.mock.lastCall![0] as any)._payload._result).toEqual(expect.objectContaining({ __test: true }));
|
||||
});
|
||||
|
||||
it("should show the recovery method removed dialog", async () => {
|
||||
const spy = jest.spyOn(Modal, "createDialog");
|
||||
jest.mock("../../../../src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog", () => ({
|
||||
__test: true,
|
||||
__esModule: true,
|
||||
default: () => <span>mocked dialog</span>,
|
||||
}));
|
||||
|
||||
getComponent({});
|
||||
defaultDispatcher.dispatch({
|
||||
action: "will_start_client",
|
||||
});
|
||||
await flushPromises();
|
||||
mockClient.emit(CryptoEvent.KeyBackupFailed, "error code");
|
||||
await waitFor(() => expect(spy).toHaveBeenCalledTimes(1));
|
||||
expect((spy.mock.lastCall![0] as any)._payload._result).toEqual(expect.objectContaining({ __test: true }));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
|
|||
|
||||
import React from "react";
|
||||
import { render, screen, waitFor } from "jest-matrix-react";
|
||||
import { jest } from "@jest/globals";
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ import {
|
|||
} from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
import dis, { defaultDispatcher } from "../../../../src/dispatcher/dispatcher";
|
||||
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import { ViewRoomPayload } from "../../../../src/dispatcher/payloads/ViewRoomPayload";
|
||||
import { RoomView as _RoomView } from "../../../../src/components/structures/RoomView";
|
||||
import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
|
||||
|
@ -527,7 +527,7 @@ describe("RoomView", () => {
|
|||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "feature_ask_to_join");
|
||||
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Knock);
|
||||
jest.spyOn(dis, "dispatch");
|
||||
jest.spyOn(defaultDispatcher, "dispatch");
|
||||
});
|
||||
|
||||
it("allows to request to join", async () => {
|
||||
|
@ -536,9 +536,9 @@ describe("RoomView", () => {
|
|||
|
||||
await mountRoomView();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Request access" }));
|
||||
await untilDispatch(Action.SubmitAskToJoin, dis);
|
||||
await untilDispatch(Action.SubmitAskToJoin, defaultDispatcher);
|
||||
|
||||
expect(dis.dispatch).toHaveBeenCalledWith({
|
||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: "submit_ask_to_join",
|
||||
roomId: room.roomId,
|
||||
opts: { reason: undefined },
|
||||
|
@ -552,9 +552,12 @@ describe("RoomView", () => {
|
|||
|
||||
await mountRoomView();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Cancel request" }));
|
||||
await untilDispatch(Action.CancelAskToJoin, dis);
|
||||
await untilDispatch(Action.CancelAskToJoin, defaultDispatcher);
|
||||
|
||||
expect(dis.dispatch).toHaveBeenCalledWith({ action: "cancel_ask_to_join", roomId: room.roomId });
|
||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: "cancel_ask_to_join",
|
||||
roomId: room.roomId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -669,7 +672,7 @@ describe("RoomView", () => {
|
|||
await waitFor(() => {
|
||||
expect(container.querySelector(".mx_RoomView_searchResultsPanel")).toBeVisible();
|
||||
});
|
||||
const prom = untilDispatch(Action.ViewRoom, dis);
|
||||
const prom = untilDispatch(Action.ViewRoom, defaultDispatcher);
|
||||
|
||||
await userEvent.hover(getByText("search term"));
|
||||
await userEvent.click(await findByLabelText("Edit"));
|
||||
|
@ -678,8 +681,8 @@ describe("RoomView", () => {
|
|||
});
|
||||
|
||||
it("fires Action.RoomLoaded", async () => {
|
||||
jest.spyOn(dis, "dispatch");
|
||||
jest.spyOn(defaultDispatcher, "dispatch");
|
||||
await mountRoomView();
|
||||
expect(dis.dispatch).toHaveBeenCalledWith({ action: Action.RoomLoaded });
|
||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: Action.RoomLoaded });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
|
|||
|
||||
import React from "react";
|
||||
import { render } from "jest-matrix-react";
|
||||
import { jest } from "@jest/globals";
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { stubClient } from "../../../test-utils";
|
||||
|
|
|
@ -10,7 +10,7 @@ import React from "react";
|
|||
import { mocked, MockedObject } from "jest-mock";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { CryptoApi, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
import { render, RenderResult } from "jest-matrix-react";
|
||||
import { fireEvent, render, RenderResult, screen } from "jest-matrix-react";
|
||||
|
||||
import { filterConsole, getMockClientWithEventEmitter, mockClientMethodsCrypto } from "../../../../test-utils";
|
||||
import LogoutDialog from "../../../../../src/components/views/dialogs/LogoutDialog";
|
||||
|
@ -61,6 +61,9 @@ describe("LogoutDialog", () => {
|
|||
const rendered = renderComponent();
|
||||
await rendered.findByText("Start using Key Backup");
|
||||
expect(rendered.container).toMatchSnapshot();
|
||||
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Manually export keys" }));
|
||||
await expect(screen.findByRole("heading", { name: "Export room keys" })).resolves.toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("when there is an error fetching backups", () => {
|
||||
|
|
|
@ -33,7 +33,6 @@ exports[`DevtoolsDialog renders the devtools dialog 1`] = `
|
|||
>
|
||||
Room ID: !id
|
||||
<div
|
||||
aria-describedby=":r2:"
|
||||
aria-label="Copy"
|
||||
class="mx_AccessibleButton mx_CopyableText_copyButton"
|
||||
role="button"
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
import React from "react";
|
||||
import { screen, render, waitFor } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
// Needed to be able to mock decodeRecoveryKey
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import * as recoveryKeyModule from "matrix-js-sdk/src/crypto-api/recovery-key";
|
||||
|
@ -17,9 +19,16 @@ import RestoreKeyBackupDialog from "../../../../../../src/components/views/dialo
|
|||
import { stubClient } from "../../../../../test-utils";
|
||||
|
||||
describe("<RestoreKeyBackupDialog />", () => {
|
||||
const keyBackupRestoreResult = {
|
||||
total: 2,
|
||||
imported: 1,
|
||||
};
|
||||
|
||||
let matrixClient: MatrixClient;
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
matrixClient = stubClient();
|
||||
jest.spyOn(recoveryKeyModule, "decodeRecoveryKey").mockReturnValue(new Uint8Array(32));
|
||||
jest.spyOn(matrixClient, "getKeyBackupVersion").mockResolvedValue({ version: "1" } as KeyBackupInfo);
|
||||
});
|
||||
|
||||
it("should render", async () => {
|
||||
|
@ -48,4 +57,71 @@ describe("<RestoreKeyBackupDialog />", () => {
|
|||
await waitFor(() => expect(screen.getByText("👍 This looks like a valid Security Key!")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should restore key backup when the key is cached", async () => {
|
||||
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackup").mockResolvedValue(keyBackupRestoreResult);
|
||||
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Successfully restored 1 keys")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should restore key backup when the key is in secret storage", async () => {
|
||||
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackup")
|
||||
// Reject when trying to restore from cache
|
||||
.mockRejectedValueOnce(new Error("key backup not found"))
|
||||
// Resolve when trying to restore from secret storage
|
||||
.mockResolvedValue(keyBackupRestoreResult);
|
||||
jest.spyOn(matrixClient.secretStorage, "hasKey").mockResolvedValue(true);
|
||||
jest.spyOn(matrixClient, "isKeyBackupKeyStored").mockResolvedValue({});
|
||||
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Successfully restored 1 keys")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should restore key backup when security key is filled by user", async () => {
|
||||
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackup")
|
||||
// Reject when trying to restore from cache
|
||||
.mockRejectedValueOnce(new Error("key backup not found"))
|
||||
// Resolve when trying to restore from recovery key
|
||||
.mockResolvedValue(keyBackupRestoreResult);
|
||||
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Enter Security Key")).toBeInTheDocument());
|
||||
|
||||
await userEvent.type(screen.getByRole("textbox"), "my security key");
|
||||
await userEvent.click(screen.getByRole("button", { name: "Next" }));
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Successfully restored 1 keys")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("should restore key backup when passphrase is filled", async () => {
|
||||
// Determine that the passphrase is required
|
||||
jest.spyOn(matrixClient, "getKeyBackupVersion").mockResolvedValue({
|
||||
version: "1",
|
||||
auth_data: {
|
||||
private_key_salt: "salt",
|
||||
private_key_iterations: 1,
|
||||
},
|
||||
} as KeyBackupInfo);
|
||||
|
||||
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackup")
|
||||
// Reject when trying to restore from cache
|
||||
.mockRejectedValue(new Error("key backup not found"));
|
||||
|
||||
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackupWithPassphrase").mockResolvedValue(
|
||||
keyBackupRestoreResult,
|
||||
);
|
||||
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Enter Security Phrase")).toBeInTheDocument());
|
||||
// Not role for password https://github.com/w3c/aria/issues/935
|
||||
await userEvent.type(screen.getByTestId("passphraseInput"), "my passphrase");
|
||||
await userEvent.click(screen.getByRole("button", { name: "Next" }));
|
||||
|
||||
await waitFor(() => expect(screen.getByText("Successfully restored 1 keys")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -296,3 +296,263 @@ exports[`<RestoreKeyBackupDialog /> should render 1`] = `
|
|||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should restore key backup when passphrase is filled 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Keys restored
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
Successfully restored 1 keys
|
||||
</p>
|
||||
<p>
|
||||
Failed to decrypt 1 sessions!
|
||||
</p>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should restore key backup when security key is filled by user 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Keys restored
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
Successfully restored 1 keys
|
||||
</p>
|
||||
<p>
|
||||
Failed to decrypt 1 sessions!
|
||||
</p>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should restore key backup when the key is cached 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Keys restored
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
Successfully restored 1 keys
|
||||
</p>
|
||||
<p>
|
||||
Failed to decrypt 1 sessions!
|
||||
</p>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should restore key backup when the key is in secret storage 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Keys restored
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
Successfully restored 1 keys
|
||||
</p>
|
||||
<p>
|
||||
Failed to decrypt 1 sessions!
|
||||
</p>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
|
|
@ -7,13 +7,11 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { jest } from "@jest/globals";
|
||||
import { Room, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { ClientWidgetApi, IWidget, MatrixWidgetType } from "matrix-widget-api";
|
||||
import { Optional } from "matrix-events-sdk";
|
||||
import { act, render, RenderResult } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { SpiedFunction } from "jest-mock";
|
||||
import {
|
||||
ApprovalOpts,
|
||||
WidgetInfo,
|
||||
|
@ -345,7 +343,7 @@ describe("AppTile", () => {
|
|||
|
||||
describe("for a pinned widget", () => {
|
||||
let renderResult: RenderResult;
|
||||
let moveToContainerSpy: SpiedFunction<typeof WidgetLayoutStore.instance.moveToContainer>;
|
||||
let moveToContainerSpy: jest.SpyInstance<void, [room: Room, widget: IWidget, toContainer: Container]>;
|
||||
|
||||
beforeEach(() => {
|
||||
renderResult = render(
|
||||
|
|
|
@ -7,13 +7,57 @@
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
import { render, fireEvent, waitFor } from "jest-matrix-react";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import ImageView from "../../../../../src/components/views/elements/ImageView";
|
||||
import { FileDownloader } from "../../../../../src/utils/FileDownloader";
|
||||
import Modal from "../../../../../src/Modal";
|
||||
import ErrorDialog from "../../../../../src/components/views/dialogs/ErrorDialog";
|
||||
|
||||
jest.mock("../../../../../src/utils/FileDownloader");
|
||||
|
||||
describe("<ImageView />", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
fetchMock.reset();
|
||||
});
|
||||
|
||||
it("renders correctly", () => {
|
||||
const { container } = render(<ImageView src="https://example.com/image.png" onFinished={jest.fn()} />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should download on click", async () => {
|
||||
fetchMock.get("https://example.com/image.png", "TESTFILE");
|
||||
const { getByRole } = render(
|
||||
<ImageView src="https://example.com/image.png" name="filename.png" onFinished={jest.fn()} />,
|
||||
);
|
||||
fireEvent.click(getByRole("button", { name: "Download" }));
|
||||
await waitFor(() =>
|
||||
expect(mocked(FileDownloader).mock.instances[0].download).toHaveBeenCalledWith({
|
||||
blob: expect.anything(),
|
||||
name: "filename.png",
|
||||
}),
|
||||
);
|
||||
expect(fetchMock).toHaveFetched("https://example.com/image.png");
|
||||
});
|
||||
|
||||
it("should handle download errors", async () => {
|
||||
const modalSpy = jest.spyOn(Modal, "createDialog");
|
||||
fetchMock.get("https://example.com/image.png", { status: 500 });
|
||||
const { getByRole } = render(
|
||||
<ImageView src="https://example.com/image.png" name="filename.png" onFinished={jest.fn()} />,
|
||||
);
|
||||
fireEvent.click(getByRole("button", { name: "Download" }));
|
||||
await waitFor(() =>
|
||||
expect(modalSpy).toHaveBeenCalledWith(
|
||||
ErrorDialog,
|
||||
expect.objectContaining({
|
||||
title: "Download failed",
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -356,8 +356,8 @@ exports[`AppTile for a pinned widget should render permission request 1`] = `
|
|||
<span>
|
||||
Using this widget may share data
|
||||
<div
|
||||
aria-describedby=":r2n:"
|
||||
aria-labelledby=":r2m:"
|
||||
aria-describedby=":r2j:"
|
||||
aria-labelledby=":r2i:"
|
||||
class="mx_TextWithTooltip_target mx_TextWithTooltip_target--helpIcon"
|
||||
>
|
||||
<svg
|
||||
|
|
|
@ -21,7 +21,6 @@ exports[`<ImageView /> renders correctly 1`] = `
|
|||
class="mx_ImageView_toolbar"
|
||||
>
|
||||
<div
|
||||
aria-describedby=":r2:"
|
||||
aria-label="Zoom out"
|
||||
class="mx_AccessibleButton mx_ImageView_button mx_ImageView_button_zoomOut"
|
||||
role="button"
|
||||
|
|
|
@ -23,7 +23,6 @@ exports[`<LocationViewDialog /> renders map correctly 1`] = `
|
|||
class="mx_ZoomButtons"
|
||||
>
|
||||
<div
|
||||
aria-describedby=":r2:"
|
||||
aria-label="Zoom in"
|
||||
class="mx_AccessibleButton mx_ZoomButtons_button"
|
||||
data-testid="map-zoom-in-button"
|
||||
|
|
|
@ -6,9 +6,18 @@ exports[`<MapError /> applies class when isMinimised is truthy 1`] = `
|
|||
class="mx_MapError test mx_MapError_isMinimised"
|
||||
data-testid="map-rendering-error"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_MapError_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 15a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 16c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-4c.283 0 .52-.096.713-.287A.968.968 0 0 0 13 12V8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8v4c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 9a9.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 22Z"
|
||||
/>
|
||||
</svg>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
|
@ -36,9 +45,18 @@ exports[`<MapError /> renders correctly for MapStyleUrlNotConfigured 1`] = `
|
|||
class="mx_MapError test"
|
||||
data-testid="map-rendering-error"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_MapError_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 15a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 16c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-4c.283 0 .52-.096.713-.287A.968.968 0 0 0 13 12V8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8v4c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 9a9.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 22Z"
|
||||
/>
|
||||
</svg>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
|
@ -66,9 +84,18 @@ exports[`<MapError /> renders correctly for MapStyleUrlNotReachable 1`] = `
|
|||
class="mx_MapError test"
|
||||
data-testid="map-rendering-error"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_MapError_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 15a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 16c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-4c.283 0 .52-.096.713-.287A.968.968 0 0 0 13 12V8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8v4c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 9a9.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 22Z"
|
||||
/>
|
||||
</svg>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
|
|
|
@ -91,6 +91,12 @@ describe("DateSeparator", () => {
|
|||
expect(getComponent({ ts, forExport: false }).container.textContent).toEqual(result);
|
||||
});
|
||||
|
||||
it("renders invalid date separator correctly", () => {
|
||||
const ts = new Date(-8640000000000004).getTime();
|
||||
const { asFragment } = getComponent({ ts });
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("when forExport is true", () => {
|
||||
it.each(testCases)("formats date in full when current time is %s", (_d, ts) => {
|
||||
expect(getComponent({ ts, forExport: true }).container.textContent).toEqual(
|
||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, render, RenderResult } from "jest-matrix-react";
|
||||
import { fireEvent, render, RenderResult, waitFor } from "jest-matrix-react";
|
||||
import {
|
||||
MatrixEvent,
|
||||
Relations,
|
||||
|
@ -83,7 +83,7 @@ describe("MPollBody", () => {
|
|||
expect(votesCount(renderResult, "poutine")).toBe("");
|
||||
expect(votesCount(renderResult, "italian")).toBe("");
|
||||
expect(votesCount(renderResult, "wings")).toBe("");
|
||||
expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("No votes cast");
|
||||
await waitFor(() => expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("No votes cast"));
|
||||
expect(renderResult.getByText("What should we order for the party?")).toBeTruthy();
|
||||
});
|
||||
|
||||
|
|
|
@ -33,6 +33,16 @@ jest.mock("../../../../../src/components/views/messages/MImageBody", () => ({
|
|||
default: () => <div data-testid="image-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/components/views/messages/MVideoBody", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="video-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/components/views/messages/MFileBody", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="file-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/components/views/messages/MImageReplyBody", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="image-reply-body" />,
|
||||
|
@ -95,8 +105,8 @@ describe("MessageEvent", () => {
|
|||
describe("when an image with a caption is sent", () => {
|
||||
let result: RenderResult;
|
||||
|
||||
beforeEach(() => {
|
||||
event = mkEvent({
|
||||
function createEvent(mimetype: string, filename: string, msgtype: string) {
|
||||
return mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
user: client.getUserId()!,
|
||||
|
@ -105,19 +115,19 @@ describe("MessageEvent", () => {
|
|||
body: "caption for a test image",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: "<strong>caption for a test image</strong>",
|
||||
msgtype: MsgType.Image,
|
||||
filename: "image.webp",
|
||||
msgtype: msgtype,
|
||||
filename: filename,
|
||||
info: {
|
||||
w: 40,
|
||||
h: 50,
|
||||
mimetype: mimetype,
|
||||
},
|
||||
url: "mxc://server/image",
|
||||
},
|
||||
});
|
||||
result = renderMessageEvent();
|
||||
});
|
||||
}
|
||||
|
||||
it("should render a TextualBody and an ImageBody", () => {
|
||||
function mockMedia() {
|
||||
fetchMock.getOnce(
|
||||
"https://server/_matrix/media/v3/download/server/image",
|
||||
{
|
||||
|
@ -125,8 +135,38 @@ describe("MessageEvent", () => {
|
|||
},
|
||||
{ sendAsJson: false },
|
||||
);
|
||||
}
|
||||
|
||||
it("should render a TextualBody and an ImageBody", () => {
|
||||
event = createEvent("image/webp", "image.webp", MsgType.Image);
|
||||
result = renderMessageEvent();
|
||||
mockMedia();
|
||||
result.getByTestId("image-body");
|
||||
result.getByTestId("textual-body");
|
||||
});
|
||||
|
||||
it("should render a TextualBody and a FileBody for mismatched extension", () => {
|
||||
event = createEvent("image/webp", "image.exe", MsgType.Image);
|
||||
result = renderMessageEvent();
|
||||
mockMedia();
|
||||
result.getByTestId("file-body");
|
||||
result.getByTestId("textual-body");
|
||||
});
|
||||
|
||||
it("should render a TextualBody and an VideoBody", () => {
|
||||
event = createEvent("video/mp4", "video.mp4", MsgType.Video);
|
||||
result = renderMessageEvent();
|
||||
mockMedia();
|
||||
result.getByTestId("video-body");
|
||||
result.getByTestId("textual-body");
|
||||
});
|
||||
|
||||
it("should render a TextualBody and a FileBody for non-video mimetype", () => {
|
||||
event = createEvent("application/octet-stream", "video.mp4", MsgType.Video);
|
||||
result = renderMessageEvent();
|
||||
mockMedia();
|
||||
result.getByTestId("file-body");
|
||||
result.getByTestId("textual-body");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,32 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`DateSeparator renders invalid date separator correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
aria-label="Invalid timestamp"
|
||||
class="mx_TimelineSeparator"
|
||||
role="separator"
|
||||
>
|
||||
<hr
|
||||
role="none"
|
||||
/>
|
||||
<div
|
||||
class="mx_DateSeparator_dateContent"
|
||||
>
|
||||
<h2
|
||||
aria-hidden="true"
|
||||
class="mx_DateSeparator_dateHeading"
|
||||
>
|
||||
Invalid timestamp
|
||||
</h2>
|
||||
</div>
|
||||
<hr
|
||||
role="none"
|
||||
/>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`DateSeparator renders the date separator correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
|
|
|
@ -5,9 +5,18 @@ exports[`<MBeaconBody /> when map display is not configured renders maps unavail
|
|||
class="mx_MapError mx_MBeaconBody_mapError mx_MBeaconBody_mapErrorInteractive mx_MapError_isMinimised"
|
||||
data-testid="map-rendering-error"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_MapError_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 15a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 16c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-4c.283 0 .52-.096.713-.287A.968.968 0 0 0 13 12V8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8v4c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 9a9.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 22Z"
|
||||
/>
|
||||
</svg>
|
||||
<h3
|
||||
class="mx_Heading_h3 mx_MapError_heading"
|
||||
>
|
||||
|
|
|
@ -23,7 +23,7 @@ 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 { flushPromises, stubClient } from "../../../../test-utils";
|
||||
import { PollHistoryDialog } from "../../../../../src/components/views/dialogs/PollHistoryDialog";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
import { _t } from "../../../../../src/languageHandler";
|
||||
|
@ -56,16 +56,7 @@ describe("<RoomSummaryCard />", () => {
|
|||
};
|
||||
|
||||
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({}),
|
||||
});
|
||||
mockClient = mocked(stubClient());
|
||||
room = new Room(roomId, mockClient, userId);
|
||||
const roomCreateEvent = new MatrixEvent({
|
||||
type: "m.room.create",
|
||||
|
|
|
@ -134,6 +134,7 @@ beforeEach(() => {
|
|||
getUserDeviceInfo: jest.fn(),
|
||||
userHasCrossSigningKeys: jest.fn().mockResolvedValue(false),
|
||||
getUserVerificationStatus: jest.fn(),
|
||||
isEncryptionEnabledInRoom: jest.fn().mockResolvedValue(false),
|
||||
} as unknown as CryptoApi);
|
||||
|
||||
mockClient = mocked({
|
||||
|
@ -148,7 +149,6 @@ beforeEach(() => {
|
|||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
isSynapseAdministrator: jest.fn().mockResolvedValue(false),
|
||||
isRoomEncrypted: jest.fn().mockReturnValue(false),
|
||||
doesServerSupportUnstableFeature: jest.fn().mockReturnValue(false),
|
||||
doesServerSupportExtendedProfiles: jest.fn().mockResolvedValue(false),
|
||||
getExtendedProfileProperty: jest.fn().mockRejectedValue(new Error("Not supported")),
|
||||
|
@ -660,7 +660,7 @@ describe("<UserInfo />", () => {
|
|||
|
||||
describe("with an encrypted room", () => {
|
||||
beforeEach(() => {
|
||||
mockClient.isRoomEncrypted.mockReturnValue(true);
|
||||
jest.spyOn(mockClient.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||
});
|
||||
|
||||
it("renders unverified user info", async () => {
|
||||
|
|
|
@ -27,7 +27,6 @@ import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
|||
import DocumentOffset from "../../../../../src/editor/offset";
|
||||
import { Layout } from "../../../../../src/settings/enums/Layout";
|
||||
import { IRoomState, MainSplitContentType } from "../../../../../src/components/structures/RoomView";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
import { mockPlatformPeg } from "../../../../test-utils/platform";
|
||||
import { doMaybeLocalRoomAction } from "../../../../../src/utils/local-room";
|
||||
import { addTextToComposer } from "../../../../test-utils/composer";
|
||||
|
@ -80,14 +79,12 @@ describe("<SendMessageComposer/>", () => {
|
|||
viewRoomOpts: { buttons: [] },
|
||||
};
|
||||
describe("createMessageContent", () => {
|
||||
const permalinkCreator = jest.fn() as any;
|
||||
|
||||
it("sends plaintext messages correctly", () => {
|
||||
const model = new EditorModel([], createPartCreator());
|
||||
const documentOffset = new DocumentOffset(11, true);
|
||||
model.update("hello world", "insertText", documentOffset);
|
||||
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined, permalinkCreator);
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined);
|
||||
|
||||
expect(content).toEqual({
|
||||
"body": "hello world",
|
||||
|
@ -101,7 +98,7 @@ describe("<SendMessageComposer/>", () => {
|
|||
const documentOffset = new DocumentOffset(13, true);
|
||||
model.update("hello *world*", "insertText", documentOffset);
|
||||
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined, permalinkCreator);
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined);
|
||||
|
||||
expect(content).toEqual({
|
||||
"body": "hello *world*",
|
||||
|
@ -117,7 +114,7 @@ describe("<SendMessageComposer/>", () => {
|
|||
const documentOffset = new DocumentOffset(22, true);
|
||||
model.update("/me blinks __quickly__", "insertText", documentOffset);
|
||||
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined, permalinkCreator);
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined);
|
||||
|
||||
expect(content).toEqual({
|
||||
"body": "blinks __quickly__",
|
||||
|
@ -134,7 +131,7 @@ describe("<SendMessageComposer/>", () => {
|
|||
model.update("/me ✨sparkles✨", "insertText", documentOffset);
|
||||
expect(model.parts.length).toEqual(4); // Emoji count as non-text
|
||||
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined, permalinkCreator);
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined);
|
||||
|
||||
expect(content).toEqual({
|
||||
"body": "✨sparkles✨",
|
||||
|
@ -149,7 +146,7 @@ describe("<SendMessageComposer/>", () => {
|
|||
|
||||
model.update("//dev/null is my favourite place", "insertText", documentOffset);
|
||||
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined, permalinkCreator);
|
||||
const content = createMessageContent("@alice:test", model, undefined, undefined);
|
||||
|
||||
expect(content).toEqual({
|
||||
"body": "/dev/null is my favourite place",
|
||||
|
@ -364,7 +361,6 @@ describe("<SendMessageComposer/>", () => {
|
|||
const defaultProps = {
|
||||
room: mockRoom,
|
||||
toggleStickerPickerOpen: jest.fn(),
|
||||
permalinkCreator: new RoomPermalinkCreator(mockRoom),
|
||||
};
|
||||
const getRawComponent = (props = {}, roomContext = defaultRoomContext, client = mockClient) => (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
|
@ -482,6 +478,44 @@ describe("<SendMessageComposer/>", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("correctly sends a reply using a slash command", async () => {
|
||||
stubClient();
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(
|
||||
<T,>(roomId: string, fn: (actualRoomId: string) => Promise<T>, _client?: MatrixClient) => {
|
||||
return fn(roomId);
|
||||
},
|
||||
);
|
||||
|
||||
const replyToEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: "@bob:test",
|
||||
room: "!abc:test",
|
||||
content: { "m.mentions": {} },
|
||||
event: true,
|
||||
});
|
||||
|
||||
mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) });
|
||||
const { container } = getComponent({ replyToEvent });
|
||||
|
||||
addTextToComposer(container, "/tableflip");
|
||||
fireEvent.keyDown(container.querySelector(".mx_SendMessageComposer")!, { key: "Enter" });
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockClient.sendMessage).toHaveBeenCalledWith("myfakeroom", null, {
|
||||
"body": "(╯°□°)╯︵ ┻━┻",
|
||||
"msgtype": MsgType.Text,
|
||||
"m.mentions": {
|
||||
user_ids: ["@bob:test"],
|
||||
},
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
event_id: replyToEvent.getId(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("shows chat effects on message sending", () => {
|
||||
mocked(doMaybeLocalRoomAction).mockImplementation(
|
||||
<T,>(roomId: string, fn: (actualRoomId: string) => Promise<T>, _client?: MatrixClient) => {
|
||||
|
|
|
@ -15,7 +15,6 @@ import VoiceRecordComposerTile from "../../../../../src/components/views/rooms/V
|
|||
import { doMaybeLocalRoomAction } from "../../../../../src/utils/local-room";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { IUpload, VoiceMessageRecording } from "../../../../../src/audio/VoiceMessageRecording";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
import { VoiceRecordingStore } from "../../../../../src/stores/VoiceRecordingStore";
|
||||
import { PlaybackClock } from "../../../../../src/audio/PlaybackClock";
|
||||
import { mkEvent } from "../../../../test-utils";
|
||||
|
@ -57,7 +56,6 @@ describe("<VoiceRecordComposerTile/>", () => {
|
|||
const props = {
|
||||
room,
|
||||
ref: voiceRecordComposerTile,
|
||||
permalinkCreator: new RoomPermalinkCreator(room),
|
||||
};
|
||||
mockUpload = {
|
||||
mxc: "mxc://example.com/voice",
|
||||
|
@ -142,7 +140,6 @@ describe("<VoiceRecordComposerTile/>", () => {
|
|||
const props = {
|
||||
room,
|
||||
ref: voiceRecordComposerTile,
|
||||
permalinkCreator: new RoomPermalinkCreator(room),
|
||||
replyToEvent,
|
||||
};
|
||||
render(<VoiceRecordComposerTile {...props} />);
|
||||
|
|
|
@ -8,26 +8,13 @@ Please see LICENSE files in the repository root for full details.
|
|||
import { MsgType } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { filterConsole, mkEvent } from "../../../../../../test-utils";
|
||||
import { RoomPermalinkCreator } from "../../../../../../../src/utils/permalinks/Permalinks";
|
||||
import {
|
||||
createMessageContent,
|
||||
EMOTE_PREFIX,
|
||||
} from "../../../../../../../src/components/views/rooms/wysiwyg_composer/utils/createMessageContent";
|
||||
|
||||
describe("createMessageContent", () => {
|
||||
const permalinkCreator = {
|
||||
forEvent(eventId: string): string {
|
||||
return "$$permalink$$";
|
||||
},
|
||||
} as RoomPermalinkCreator;
|
||||
const message = "<em><b>hello</b> world</em>";
|
||||
const mockEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
room: "myfakeroom",
|
||||
user: "myfakeuser",
|
||||
content: { msgtype: "m.text", body: "Replying to this" },
|
||||
event: true,
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
@ -42,12 +29,12 @@ describe("createMessageContent", () => {
|
|||
// Warm up by creating the component once, with a long timeout.
|
||||
// This prevents tests timing out because of the time spent loading
|
||||
// the WASM component.
|
||||
await createMessageContent(message, true, { permalinkCreator });
|
||||
await createMessageContent(message, true, {});
|
||||
}, 10000);
|
||||
|
||||
it("Should create html message", async () => {
|
||||
// When
|
||||
const content = await createMessageContent(message, true, { permalinkCreator });
|
||||
const content = await createMessageContent(message, true, {});
|
||||
|
||||
// Then
|
||||
expect(content).toEqual({
|
||||
|
@ -58,34 +45,13 @@ describe("createMessageContent", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("Should add reply to message content", async () => {
|
||||
// When
|
||||
const content = await createMessageContent(message, true, { permalinkCreator, replyToEvent: mockEvent });
|
||||
|
||||
// Then
|
||||
expect(content).toEqual({
|
||||
"body": "> <myfakeuser> Replying to this\n\n*__hello__ world*",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body":
|
||||
'<mx-reply><blockquote><a href="$$permalink$$">In reply to</a>' +
|
||||
' <a href="https://matrix.to/#/myfakeuser">myfakeuser</a>' +
|
||||
"<br>Replying to this</blockquote></mx-reply><em><b>hello</b> world</em>",
|
||||
"msgtype": "m.text",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
event_id: mockEvent.getId(),
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("Should add relation to message", async () => {
|
||||
// When
|
||||
const relation = {
|
||||
rel_type: "m.thread",
|
||||
event_id: "myFakeThreadId",
|
||||
};
|
||||
const content = await createMessageContent(message, true, { permalinkCreator, relation });
|
||||
const content = await createMessageContent(message, true, { relation });
|
||||
|
||||
// Then
|
||||
expect(content).toEqual({
|
||||
|
@ -118,7 +84,7 @@ describe("createMessageContent", () => {
|
|||
},
|
||||
event: true,
|
||||
});
|
||||
const content = await createMessageContent(message, true, { permalinkCreator, editedEvent });
|
||||
const content = await createMessageContent(message, true, { editedEvent });
|
||||
|
||||
// Then
|
||||
expect(content).toEqual({
|
||||
|
@ -141,20 +107,20 @@ describe("createMessageContent", () => {
|
|||
|
||||
it("Should strip the /me prefix from a message", async () => {
|
||||
const textBody = "some body text";
|
||||
const content = await createMessageContent(EMOTE_PREFIX + textBody, true, { permalinkCreator });
|
||||
const content = await createMessageContent(EMOTE_PREFIX + textBody, true, {});
|
||||
|
||||
expect(content).toMatchObject({ body: textBody, formatted_body: textBody });
|
||||
});
|
||||
|
||||
it("Should strip single / from message prefixed with //", async () => {
|
||||
const content = await createMessageContent("//twoSlashes", true, { permalinkCreator });
|
||||
const content = await createMessageContent("//twoSlashes", true, {});
|
||||
|
||||
expect(content).toMatchObject({ body: "/twoSlashes", formatted_body: "/twoSlashes" });
|
||||
});
|
||||
|
||||
it("Should set the content type to MsgType.Emote when /me prefix is used", async () => {
|
||||
const textBody = "some body text";
|
||||
const content = await createMessageContent(EMOTE_PREFIX + textBody, true, { permalinkCreator });
|
||||
const content = await createMessageContent(EMOTE_PREFIX + textBody, true, {});
|
||||
|
||||
expect(content).toMatchObject({ msgtype: MsgType.Emote });
|
||||
});
|
||||
|
@ -164,14 +130,14 @@ describe("createMessageContent", () => {
|
|||
it("Should replace at-room mentions with `@room` in body", async () => {
|
||||
const messageComposerState = `<a href="#" contenteditable="false" data-mention-type="at-room" style="some styling">@room</a> `;
|
||||
|
||||
const content = await createMessageContent(messageComposerState, false, { permalinkCreator });
|
||||
const content = await createMessageContent(messageComposerState, false, {});
|
||||
expect(content).toMatchObject({ body: "@room " });
|
||||
});
|
||||
|
||||
it("Should replace user mentions with user name in body", async () => {
|
||||
const messageComposerState = `<a href="https://matrix.to/#/@test_user:element.io" contenteditable="false" data-mention-type="user" style="some styling">a test user</a> `;
|
||||
|
||||
const content = await createMessageContent(messageComposerState, false, { permalinkCreator });
|
||||
const content = await createMessageContent(messageComposerState, false, {});
|
||||
|
||||
expect(content).toMatchObject({ body: "a test user " });
|
||||
});
|
||||
|
@ -179,7 +145,7 @@ describe("createMessageContent", () => {
|
|||
it("Should replace room mentions with room mxid in body", async () => {
|
||||
const messageComposerState = `<a href="https://matrix.to/#/#test_room:element.io" contenteditable="false" data-mention-type="room" style="some styling">a test room</a> `;
|
||||
|
||||
const content = await createMessageContent(messageComposerState, false, { permalinkCreator });
|
||||
const content = await createMessageContent(messageComposerState, false, {});
|
||||
|
||||
expect(content).toMatchObject({
|
||||
body: "#test_room:element.io ",
|
||||
|
|
|
@ -17,7 +17,6 @@ import { createTestClient, getRoomContext, mkEvent, mkStubRoom } from "../../../
|
|||
import defaultDispatcher from "../../../../../../../src/dispatcher/dispatcher";
|
||||
import SettingsStore from "../../../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../../../src/settings/SettingLevel";
|
||||
import { RoomPermalinkCreator } from "../../../../../../../src/utils/permalinks/Permalinks";
|
||||
import EditorStateTransfer from "../../../../../../../src/utils/EditorStateTransfer";
|
||||
import * as ConfirmRedactDialog from "../../../../../../../src/components/views/dialogs/ConfirmRedactDialog";
|
||||
import * as SlashCommands from "../../../../../../../src/SlashCommands";
|
||||
|
@ -27,11 +26,6 @@ import { MatrixClientPeg } from "../../../../../../../src/MatrixClientPeg";
|
|||
import { Action } from "../../../../../../../src/dispatcher/actions";
|
||||
|
||||
describe("message", () => {
|
||||
const permalinkCreator = {
|
||||
forEvent(eventId: string): string {
|
||||
return "$$permalink$$";
|
||||
},
|
||||
} as RoomPermalinkCreator;
|
||||
const message = "<i><b>hello</b> world</i>";
|
||||
const mockEvent = mkEvent({
|
||||
type: "m.room.message",
|
||||
|
@ -71,7 +65,7 @@ describe("message", () => {
|
|||
describe("sendMessage", () => {
|
||||
it("Should not send empty html message", async () => {
|
||||
// When
|
||||
await sendMessage("", true, { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator });
|
||||
await sendMessage("", true, { roomContext: defaultRoomContext, mxClient: mockClient });
|
||||
|
||||
// Then
|
||||
expect(mockClient.sendMessage).toHaveBeenCalledTimes(0);
|
||||
|
@ -86,7 +80,6 @@ describe("message", () => {
|
|||
await sendMessage(message, true, {
|
||||
roomContext: mockRoomContextWithoutId,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// Then
|
||||
|
@ -100,7 +93,6 @@ describe("message", () => {
|
|||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// Then
|
||||
|
@ -111,7 +103,6 @@ describe("message", () => {
|
|||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
relation: {},
|
||||
});
|
||||
|
||||
|
@ -123,7 +114,6 @@ describe("message", () => {
|
|||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
relation: {
|
||||
event_id: "valid_id",
|
||||
rel_type: "m.does_not_match",
|
||||
|
@ -139,7 +129,6 @@ describe("message", () => {
|
|||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
relation: {
|
||||
event_id: "valid_id",
|
||||
rel_type: "m.thread",
|
||||
|
@ -156,7 +145,6 @@ describe("message", () => {
|
|||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// Then
|
||||
|
@ -183,7 +171,6 @@ describe("message", () => {
|
|||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
replyToEvent: mockReplyEvent,
|
||||
});
|
||||
|
||||
|
@ -195,12 +182,9 @@ describe("message", () => {
|
|||
});
|
||||
|
||||
const expectedContent = {
|
||||
"body": "> <myfakeuser2> My reply\n\n*__hello__ world*",
|
||||
"body": "*__hello__ world*",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body":
|
||||
'<mx-reply><blockquote><a href="$$permalink$$">In reply to</a>' +
|
||||
' <a href="https://matrix.to/#/myfakeuser2">myfakeuser2</a>' +
|
||||
"<br>My reply</blockquote></mx-reply><i><b>hello</b> world</i>",
|
||||
"formatted_body": "<i><b>hello</b> world</i>",
|
||||
"msgtype": "m.text",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
|
@ -217,7 +201,6 @@ describe("message", () => {
|
|||
await sendMessage(message, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// Then
|
||||
|
@ -229,7 +212,7 @@ describe("message", () => {
|
|||
|
||||
it("Should handle emojis", async () => {
|
||||
// When
|
||||
await sendMessage("🎉", false, { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator });
|
||||
await sendMessage("🎉", false, { roomContext: defaultRoomContext, mxClient: mockClient });
|
||||
|
||||
// Then
|
||||
expect(spyDispatcher).toHaveBeenCalledWith({ action: "effects.confetti" });
|
||||
|
@ -244,7 +227,6 @@ describe("message", () => {
|
|||
await sendMessage(validCommand, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// Then
|
||||
|
@ -257,7 +239,6 @@ describe("message", () => {
|
|||
await sendMessage(invalidPrefixCommand, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// Then
|
||||
|
@ -275,7 +256,6 @@ describe("message", () => {
|
|||
const result = await sendMessage(validCommand, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// Then
|
||||
|
@ -290,7 +270,6 @@ describe("message", () => {
|
|||
await sendMessage(inputText, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
expect(mockClient.sendMessage).toHaveBeenCalledWith(
|
||||
"myfakeroom",
|
||||
|
@ -309,7 +288,6 @@ describe("message", () => {
|
|||
await sendMessage(inputText, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
relation: mockRelation,
|
||||
});
|
||||
|
||||
|
@ -326,7 +304,6 @@ describe("message", () => {
|
|||
await sendMessage("input", true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
replyToEvent: mockEvent,
|
||||
});
|
||||
|
||||
|
@ -341,7 +318,6 @@ describe("message", () => {
|
|||
const result = await sendMessage(input, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
replyToEvent: mockEvent,
|
||||
});
|
||||
|
||||
|
@ -357,7 +333,6 @@ describe("message", () => {
|
|||
await sendMessage(invalidCommandInput, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
// we expect the message to have been sent
|
||||
|
@ -378,7 +353,6 @@ describe("message", () => {
|
|||
const result = await sendMessage(invalidCommandInput, true, {
|
||||
roomContext: defaultRoomContext,
|
||||
mxClient: mockClient,
|
||||
permalinkCreator,
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
|
||||
import { render, screen, waitFor } from "jest-matrix-react";
|
||||
import { MatrixClient, ThreepidMedium } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixClient, MatrixError, ThreepidMedium } from "matrix-js-sdk/src/matrix";
|
||||
import React from "react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { mocked } from "jest-mock";
|
||||
|
@ -16,6 +16,7 @@ import { AddRemoveThreepids } from "../../../../../src/components/views/settings
|
|||
import { clearAllModals, stubClient } from "../../../../test-utils";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import Modal from "../../../../../src/Modal";
|
||||
import InteractiveAuthDialog from "../../../../../src/components/views/dialogs/InteractiveAuthDialog.tsx";
|
||||
|
||||
const MOCK_IDENTITY_ACCESS_TOKEN = "mock_identity_access_token";
|
||||
const mockGetAccessToken = jest.fn().mockResolvedValue(MOCK_IDENTITY_ACCESS_TOKEN);
|
||||
|
@ -222,13 +223,13 @@ describe("AddRemoveThreepids", () => {
|
|||
|
||||
const continueButton = await screen.findByRole("button", { name: /Continue/ });
|
||||
|
||||
await expect(continueButton).toHaveAttribute("aria-disabled", "true");
|
||||
expect(continueButton).toHaveAttribute("aria-disabled", "true");
|
||||
|
||||
await expect(
|
||||
await screen.findByText(
|
||||
screen.findByText(
|
||||
`A text message has been sent to +${PHONE1.address}. Please enter the verification code it contains.`,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
expect(client.requestAdd3pidMsisdnToken).toHaveBeenCalledWith(
|
||||
"GB",
|
||||
|
@ -481,4 +482,118 @@ describe("AddRemoveThreepids", () => {
|
|||
expect(client.unbindThreePid).toHaveBeenCalledWith(ThreepidMedium.Phone, PHONE1.address);
|
||||
expect(onChangeFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should show UIA dialog when necessary for adding email", async () => {
|
||||
const onChangeFn = jest.fn();
|
||||
const createDialogFn = jest.spyOn(Modal, "createDialog");
|
||||
mocked(client.requestAdd3pidEmailToken).mockResolvedValue({ sid: "1" });
|
||||
|
||||
render(
|
||||
<AddRemoveThreepids
|
||||
mode="hs"
|
||||
medium={ThreepidMedium.Email}
|
||||
threepids={[]}
|
||||
isLoading={false}
|
||||
onChange={onChangeFn}
|
||||
/>,
|
||||
{
|
||||
wrapper: clientProviderWrapper,
|
||||
},
|
||||
);
|
||||
|
||||
const input = screen.getByRole("textbox", { name: "Email Address" });
|
||||
await userEvent.type(input, EMAIL1.address);
|
||||
const addButton = screen.getByRole("button", { name: "Add" });
|
||||
await userEvent.click(addButton);
|
||||
|
||||
const continueButton = screen.getByRole("button", { name: "Continue" });
|
||||
|
||||
expect(continueButton).toBeEnabled();
|
||||
|
||||
mocked(client).addThreePidOnly.mockRejectedValueOnce(
|
||||
new MatrixError({ errcode: "M_UNAUTHORIZED", flows: [{ stages: [] }] }, 401),
|
||||
);
|
||||
|
||||
await userEvent.click(continueButton);
|
||||
|
||||
expect(createDialogFn).toHaveBeenCalledWith(
|
||||
InteractiveAuthDialog,
|
||||
expect.objectContaining({
|
||||
title: "Add Email Address",
|
||||
makeRequest: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should show UIA dialog when necessary for adding msisdn", async () => {
|
||||
const onChangeFn = jest.fn();
|
||||
const createDialogFn = jest.spyOn(Modal, "createDialog");
|
||||
mocked(client.requestAdd3pidMsisdnToken).mockResolvedValue({
|
||||
sid: "1",
|
||||
msisdn: PHONE1.address,
|
||||
intl_fmt: PHONE1.address,
|
||||
success: true,
|
||||
submit_url: "https://some-url",
|
||||
});
|
||||
|
||||
render(
|
||||
<AddRemoveThreepids
|
||||
mode="hs"
|
||||
medium={ThreepidMedium.Phone}
|
||||
threepids={[]}
|
||||
isLoading={false}
|
||||
onChange={onChangeFn}
|
||||
/>,
|
||||
{
|
||||
wrapper: clientProviderWrapper,
|
||||
},
|
||||
);
|
||||
|
||||
const countryDropdown = screen.getByRole("button", { name: /Country Dropdown/ });
|
||||
await userEvent.click(countryDropdown);
|
||||
const gbOption = screen.getByRole("option", { name: "🇬🇧 United Kingdom (+44)" });
|
||||
await userEvent.click(gbOption);
|
||||
|
||||
const input = screen.getByRole("textbox", { name: "Phone Number" });
|
||||
await userEvent.type(input, PHONE1_LOCALNUM);
|
||||
|
||||
const addButton = screen.getByRole("button", { name: "Add" });
|
||||
await userEvent.click(addButton);
|
||||
|
||||
const continueButton = screen.getByRole("button", { name: "Continue" });
|
||||
|
||||
expect(continueButton).toHaveAttribute("aria-disabled", "true");
|
||||
|
||||
await expect(
|
||||
screen.findByText(
|
||||
`A text message has been sent to +${PHONE1.address}. Please enter the verification code it contains.`,
|
||||
),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
expect(client.requestAdd3pidMsisdnToken).toHaveBeenCalledWith(
|
||||
"GB",
|
||||
PHONE1_LOCALNUM,
|
||||
client.generateClientSecret(),
|
||||
1,
|
||||
);
|
||||
|
||||
const verificationInput = screen.getByRole("textbox", { name: "Verification code" });
|
||||
await userEvent.type(verificationInput, "123456");
|
||||
|
||||
expect(continueButton).not.toHaveAttribute("aria-disabled", "true");
|
||||
|
||||
mocked(client).addThreePidOnly.mockRejectedValueOnce(
|
||||
new MatrixError({ errcode: "M_UNAUTHORIZED", flows: [{ stages: [] }] }, 401),
|
||||
);
|
||||
|
||||
await userEvent.click(continueButton);
|
||||
|
||||
expect(createDialogFn).toHaveBeenCalledWith(
|
||||
InteractiveAuthDialog,
|
||||
expect.objectContaining({
|
||||
title: "Add Phone Number",
|
||||
makeRequest: expect.any(Function),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, waitFor } from "jest-matrix-react";
|
||||
import { render, waitFor, screen, fireEvent } from "jest-matrix-react";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
|
@ -64,4 +64,34 @@ describe("CryptographyPanel", () => {
|
|||
// Then "not supported key
|
||||
await waitFor(() => expect(codes[1].innerHTML).toEqual("<strong><not supported></strong>"));
|
||||
});
|
||||
|
||||
it("should open the export e2e keys dialog on click", async () => {
|
||||
const sessionId = "ABCDEFGHIJ";
|
||||
const sessionKey = "AbCDeFghIJK7L/m4nOPqRSTUVW4xyzaBCDef6gHIJkl";
|
||||
|
||||
TestUtils.stubClient();
|
||||
const client: MatrixClient = MatrixClientPeg.safeGet();
|
||||
client.deviceId = sessionId;
|
||||
|
||||
mocked(client.getCrypto()!.getOwnDeviceKeys).mockResolvedValue({ ed25519: sessionKey, curve25519: "1234" });
|
||||
|
||||
render(<CryptographyPanel />, withClientContextRenderOptions(client));
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Export E2E room keys" }));
|
||||
await expect(screen.findByRole("heading", { name: "Export room keys" })).resolves.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should open the import e2e keys dialog on click", async () => {
|
||||
const sessionId = "ABCDEFGHIJ";
|
||||
const sessionKey = "AbCDeFghIJK7L/m4nOPqRSTUVW4xyzaBCDef6gHIJkl";
|
||||
|
||||
TestUtils.stubClient();
|
||||
const client: MatrixClient = MatrixClientPeg.safeGet();
|
||||
client.deviceId = sessionId;
|
||||
|
||||
mocked(client.getCrypto()!.getOwnDeviceKeys).mockResolvedValue({ ed25519: sessionKey, curve25519: "1234" });
|
||||
|
||||
render(<CryptographyPanel />, withClientContextRenderOptions(client));
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Import E2E room keys" }));
|
||||
await expect(screen.findByRole("heading", { name: "Import room keys" })).resolves.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -59,7 +59,7 @@ describe("<JoinRuleSettings />", () => {
|
|||
onError: jest.fn(),
|
||||
};
|
||||
const getComponent = (props: Partial<JoinRuleSettingsProps> = {}) =>
|
||||
render(<JoinRuleSettings {...defaultProps} {...props} />);
|
||||
render(<JoinRuleSettings {...defaultProps} {...props} />, { legacyRoot: false });
|
||||
|
||||
const setRoomStateEvents = (
|
||||
room: Room,
|
||||
|
|
|
@ -130,10 +130,8 @@ describe("<SecureBackupPanel />", () => {
|
|||
})
|
||||
.mockResolvedValue(null);
|
||||
getComponent();
|
||||
// flush checkKeyBackup promise
|
||||
await flushPromises();
|
||||
|
||||
fireEvent.click(screen.getByText("Delete Backup"));
|
||||
fireEvent.click(await screen.findByText("Delete Backup"));
|
||||
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
|
||||
|
|
|
@ -263,9 +263,18 @@ exports[`<CurrentDeviceSection /> renders device and correct security card when
|
|||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_DeviceExpandDetailsButton_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.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.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -416,9 +425,18 @@ exports[`<CurrentDeviceSection /> renders device and correct security card when
|
|||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_DeviceExpandDetailsButton_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.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.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,9 +9,18 @@ exports[`<DeviceExpandDetailsButton /> renders when expanded 1`] = `
|
|||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_DeviceExpandDetailsButton_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.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.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>,
|
||||
}
|
||||
|
@ -26,9 +35,18 @@ exports[`<DeviceExpandDetailsButton /> renders when not expanded 1`] = `
|
|||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_DeviceExpandDetailsButton_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.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.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>,
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
|||
import { mocked } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import DiscoverySettings from "../../../../../../src/components/views/settings/discovery/DiscoverySettings";
|
||||
import { DiscoverySettings } from "../../../../../../src/components/views/settings/discovery/DiscoverySettings";
|
||||
import { stubClient } from "../../../../../test-utils";
|
||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||
import { UIFeature } from "../../../../../../src/settings/UIFeature";
|
||||
|
|
|
@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
import React from "react";
|
||||
import { render } from "jest-matrix-react";
|
||||
|
||||
import SettingsSubsection from "../../../../../../src/components/views/settings/shared/SettingsSubsection";
|
||||
import { SettingsSubsection } from "../../../../../../src/components/views/settings/shared/SettingsSubsection";
|
||||
|
||||
describe("<SettingsSubsection />", () => {
|
||||
const defaultProps = {
|
||||
|
|
|
@ -163,9 +163,18 @@ exports[`<SessionManagerTab /> current session section renders current session s
|
|||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_DeviceExpandDetailsButton_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.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.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -302,9 +311,18 @@ exports[`<SessionManagerTab /> current session section renders current session s
|
|||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
<svg
|
||||
class="mx_DeviceExpandDetailsButton_icon"
|
||||
/>
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.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.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 460 B |
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="19px" height="19px" viewBox="0 0 19 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 39.1 (31720) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>ED5D3E59-2561-4AC1-9B43-82FBC51767FC</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<defs></defs>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon_context">
|
||||
<g>
|
||||
<path d="M9.5,19 C14.7467051,19 19,14.7467051 19,9.5 C19,4.25329488 14.7467051,0 9.5,0 C4.25329488,0 0,4.25329488 0,9.5 C0,14.7467051 4.25329488,19 9.5,19 Z" id="Oval-69" fill="#ECECEC"></path>
|
||||
<path d="M4.5,9.50063771 C4.5,9.13148623 4.59887838,8.85242947 4.7966381,8.66345907 C4.99439782,8.47448867 5.28224377,8.38000488 5.66018457,8.38000488 C6.0249414,8.38000488 6.3072941,8.47668596 6.50725115,8.67005103 C6.70720821,8.86341609 6.80718523,9.14027555 6.80718523,9.50063771 C6.80718523,9.84781589 6.70610956,10.1213794 6.50395517,10.3213365 C6.30180079,10.5212935 6.02054674,10.6212705 5.66018457,10.6212705 C5.29103309,10.6212705 5.00538444,10.5234908 4.80323006,10.3279284 C4.60107568,10.132366 4.5,9.85660521 4.5,9.50063771 L4.5,9.50063771 Z M8.3431114,9.50063771 C8.3431114,9.13148623 8.44198978,8.85242947 8.63974951,8.66345907 C8.83750923,8.47448867 9.12755247,8.38000488 9.50988794,8.38000488 C9.87464476,8.38000488 10.1569975,8.47668596 10.3569545,8.67005103 C10.5569116,8.86341609 10.6568886,9.14027555 10.6568886,9.50063771 C10.6568886,9.84781589 10.5558129,10.1213794 10.3536585,10.3213365 C10.1515042,10.5212935 9.8702501,10.6212705 9.50988794,10.6212705 C9.13634179,10.6212705 8.84849585,10.5234908 8.64634146,10.3279284 C8.44418708,10.132366 8.3431114,9.85660521 8.3431114,9.50063771 L8.3431114,9.50063771 Z M12.1928148,9.50063771 C12.1928148,9.13148623 12.2916931,8.85242947 12.4894529,8.66345907 C12.6872126,8.47448867 12.9750585,8.38000488 13.3529993,8.38000488 C13.7177562,8.38000488 14.0001089,8.47668596 14.2000659,8.67005103 C14.400023,8.86341609 14.5,9.14027555 14.5,9.50063771 C14.5,9.84781589 14.3989243,10.1213794 14.1967699,10.3213365 C13.9946156,10.5212935 13.7133615,10.6212705 13.3529993,10.6212705 C12.9838479,10.6212705 12.6981992,10.5234908 12.4960448,10.3279284 C12.2938904,10.132366 12.1928148,9.85660521 12.1928148,9.50063771 L12.1928148,9.50063771 Z" id="…" fill="#9B9B9B"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
|
@ -13,7 +13,7 @@ import { createTestClient, flushPromises, setupAsyncStoreWithClient } from "../.
|
|||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
import { BreadcrumbsStore } from "../../../src/stores/BreadcrumbsStore";
|
||||
import { Action } from "../../../src/dispatcher/actions";
|
||||
import { defaultDispatcher } from "../../../src/dispatcher/dispatcher";
|
||||
import defaultDispatcher from "../../../src/dispatcher/dispatcher";
|
||||
|
||||
describe("BreadcrumbsStore", () => {
|
||||
let store: BreadcrumbsStore;
|
||||
|
|
|
@ -8,7 +8,14 @@ Please see LICENSE files in the repository root for full details.
|
|||
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import { last } from "lodash";
|
||||
import { MatrixEvent, MatrixClient, ClientEvent, EventTimeline } from "matrix-js-sdk/src/matrix";
|
||||
import {
|
||||
MatrixEvent,
|
||||
MatrixClient,
|
||||
ClientEvent,
|
||||
EventTimeline,
|
||||
EventType,
|
||||
MatrixEventEvent,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { ClientWidgetApi, WidgetApiFromWidgetAction } from "matrix-widget-api";
|
||||
import { waitFor } from "jest-matrix-react";
|
||||
|
||||
|
@ -134,6 +141,46 @@ describe("StopGapWidget", () => {
|
|||
expect(messaging.feedEvent).toHaveBeenLastCalledWith(event2.getEffectiveEvent(), "!1:example.org");
|
||||
});
|
||||
|
||||
it("feeds decrypted events asynchronously", async () => {
|
||||
const event1Encrypted = new MatrixEvent({
|
||||
event_id: event1.getId(),
|
||||
type: EventType.RoomMessageEncrypted,
|
||||
sender: event1.sender?.userId,
|
||||
room_id: event1.getRoomId(),
|
||||
content: {},
|
||||
});
|
||||
const decryptingSpy1 = jest.spyOn(event1Encrypted, "isBeingDecrypted").mockReturnValue(true);
|
||||
client.emit(ClientEvent.Event, event1Encrypted);
|
||||
const event2Encrypted = new MatrixEvent({
|
||||
event_id: event2.getId(),
|
||||
type: EventType.RoomMessageEncrypted,
|
||||
sender: event2.sender?.userId,
|
||||
room_id: event2.getRoomId(),
|
||||
content: {},
|
||||
});
|
||||
const decryptingSpy2 = jest.spyOn(event2Encrypted, "isBeingDecrypted").mockReturnValue(true);
|
||||
client.emit(ClientEvent.Event, event2Encrypted);
|
||||
expect(messaging.feedEvent).not.toHaveBeenCalled();
|
||||
|
||||
// "Decrypt" the events, but in reverse order; first event 2…
|
||||
event2Encrypted.event.type = event2.getType();
|
||||
event2Encrypted.event.content = event2.getContent();
|
||||
decryptingSpy2.mockReturnValue(false);
|
||||
client.emit(MatrixEventEvent.Decrypted, event2Encrypted);
|
||||
expect(messaging.feedEvent).toHaveBeenCalledTimes(1);
|
||||
expect(messaging.feedEvent).toHaveBeenLastCalledWith(event2Encrypted.getEffectiveEvent(), "!1:example.org");
|
||||
// …then event 1
|
||||
event1Encrypted.event.type = event1.getType();
|
||||
event1Encrypted.event.content = event1.getContent();
|
||||
decryptingSpy1.mockReturnValue(false);
|
||||
client.emit(MatrixEventEvent.Decrypted, event1Encrypted);
|
||||
// The events should be fed in that same order so that event 2
|
||||
// doesn't have to be blocked on the decryption of event 1 (or
|
||||
// worse, dropped)
|
||||
expect(messaging.feedEvent).toHaveBeenCalledTimes(2);
|
||||
expect(messaging.feedEvent).toHaveBeenLastCalledWith(event1Encrypted.getEffectiveEvent(), "!1:example.org");
|
||||
});
|
||||
|
||||
it("should not feed incoming event if not in timeline", () => {
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
|
|
22
test/unit-tests/utils/login-test.ts
Normal file
22
test/unit-tests/utils/login-test.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
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 MatrixChat from "../../../src/components/structures/MatrixChat.tsx";
|
||||
import { isLoggedIn } from "../../../src/utils/login.ts";
|
||||
import Views from "../../../src/Views.ts";
|
||||
|
||||
describe("isLoggedIn", () => {
|
||||
it("should return true if MatrixChat state view is LOGGED_IN", () => {
|
||||
window.matrixChat = {
|
||||
state: {
|
||||
view: Views.LOGGED_IN,
|
||||
},
|
||||
} as unknown as MatrixChat;
|
||||
|
||||
expect(isLoggedIn()).toBe(true);
|
||||
});
|
||||
});
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render } from "jest-matrix-react";
|
||||
import { act, render } from "jest-matrix-react";
|
||||
import { MatrixEvent, ConditionKind, EventType, PushRuleActionName, Room, TweakName } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
|
@ -15,6 +15,7 @@ import { pillifyLinks } from "../../../src/utils/pillify";
|
|||
import { stubClient } from "../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
||||
import DMRoomMap from "../../../src/utils/DMRoomMap";
|
||||
import { ReactRootManager } from "../../../src/utils/react.tsx";
|
||||
|
||||
describe("pillify", () => {
|
||||
const roomId = "!room:id";
|
||||
|
@ -84,51 +85,55 @@ describe("pillify", () => {
|
|||
it("should do nothing for empty element", () => {
|
||||
const { container } = render(<div />);
|
||||
const originalHtml = container.outerHTML;
|
||||
const containers: Element[] = [];
|
||||
const containers = new ReactRootManager();
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
expect(containers).toHaveLength(0);
|
||||
expect(containers.elements).toHaveLength(0);
|
||||
expect(container.outerHTML).toEqual(originalHtml);
|
||||
});
|
||||
|
||||
it("should pillify @room", () => {
|
||||
const { container } = render(<div>@room</div>);
|
||||
const containers: Element[] = [];
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
expect(containers).toHaveLength(1);
|
||||
const containers = new ReactRootManager();
|
||||
act(() => pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers));
|
||||
expect(containers.elements).toHaveLength(1);
|
||||
expect(container.querySelector(".mx_Pill.mx_AtRoomPill")?.textContent).toBe("!@room");
|
||||
});
|
||||
|
||||
it("should pillify @room in an intentional mentions world", () => {
|
||||
mocked(MatrixClientPeg.safeGet().supportsIntentionalMentions).mockReturnValue(true);
|
||||
const { container } = render(<div>@room</div>);
|
||||
const containers: Element[] = [];
|
||||
pillifyLinks(
|
||||
MatrixClientPeg.safeGet(),
|
||||
[container],
|
||||
new MatrixEvent({
|
||||
room_id: roomId,
|
||||
type: EventType.RoomMessage,
|
||||
content: {
|
||||
"body": "@room",
|
||||
"m.mentions": {
|
||||
room: true,
|
||||
const containers = new ReactRootManager();
|
||||
act(() =>
|
||||
pillifyLinks(
|
||||
MatrixClientPeg.safeGet(),
|
||||
[container],
|
||||
new MatrixEvent({
|
||||
room_id: roomId,
|
||||
type: EventType.RoomMessage,
|
||||
content: {
|
||||
"body": "@room",
|
||||
"m.mentions": {
|
||||
room: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
containers,
|
||||
}),
|
||||
containers,
|
||||
),
|
||||
);
|
||||
expect(containers).toHaveLength(1);
|
||||
expect(containers.elements).toHaveLength(1);
|
||||
expect(container.querySelector(".mx_Pill.mx_AtRoomPill")?.textContent).toBe("!@room");
|
||||
});
|
||||
|
||||
it("should not double up pillification on repeated calls", () => {
|
||||
const { container } = render(<div>@room</div>);
|
||||
const containers: Element[] = [];
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
expect(containers).toHaveLength(1);
|
||||
const containers = new ReactRootManager();
|
||||
act(() => {
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
pillifyLinks(MatrixClientPeg.safeGet(), [container], event, containers);
|
||||
});
|
||||
expect(containers.elements).toHaveLength(1);
|
||||
expect(container.querySelector(".mx_Pill.mx_AtRoomPill")?.textContent).toBe("!@room");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ import { act, render } from "jest-matrix-react";
|
|||
import { tooltipifyLinks } from "../../../src/utils/tooltipify";
|
||||
import PlatformPeg from "../../../src/PlatformPeg";
|
||||
import BasePlatform from "../../../src/BasePlatform";
|
||||
import { ReactRootManager } from "../../../src/utils/react.tsx";
|
||||
|
||||
describe("tooltipify", () => {
|
||||
jest.spyOn(PlatformPeg, "get").mockReturnValue({ needsUrlTooltips: () => true } as unknown as BasePlatform);
|
||||
|
@ -19,9 +20,9 @@ describe("tooltipify", () => {
|
|||
it("does nothing for empty element", () => {
|
||||
const { container: root } = render(<div />);
|
||||
const originalHtml = root.outerHTML;
|
||||
const containers: Element[] = [];
|
||||
const containers = new ReactRootManager();
|
||||
tooltipifyLinks([root], [], containers);
|
||||
expect(containers).toHaveLength(0);
|
||||
expect(containers.elements).toHaveLength(0);
|
||||
expect(root.outerHTML).toEqual(originalHtml);
|
||||
});
|
||||
|
||||
|
@ -31,9 +32,9 @@ describe("tooltipify", () => {
|
|||
<a href="/foo">click</a>
|
||||
</div>,
|
||||
);
|
||||
const containers: Element[] = [];
|
||||
const containers = new ReactRootManager();
|
||||
tooltipifyLinks([root], [], containers);
|
||||
expect(containers).toHaveLength(1);
|
||||
expect(containers.elements).toHaveLength(1);
|
||||
const anchor = root.querySelector("a");
|
||||
expect(anchor?.getAttribute("href")).toEqual("/foo");
|
||||
const tooltip = anchor!.querySelector(".mx_TextWithTooltip_target");
|
||||
|
@ -47,9 +48,9 @@ describe("tooltipify", () => {
|
|||
</div>,
|
||||
);
|
||||
const originalHtml = root.outerHTML;
|
||||
const containers: Element[] = [];
|
||||
const containers = new ReactRootManager();
|
||||
tooltipifyLinks([root], [root.children[0]], containers);
|
||||
expect(containers).toHaveLength(0);
|
||||
expect(containers.elements).toHaveLength(0);
|
||||
expect(root.outerHTML).toEqual(originalHtml);
|
||||
});
|
||||
|
||||
|
@ -59,12 +60,12 @@ describe("tooltipify", () => {
|
|||
<a href="/foo">click</a>
|
||||
</div>,
|
||||
);
|
||||
const containers: Element[] = [];
|
||||
const containers = new ReactRootManager();
|
||||
tooltipifyLinks([root], [], containers);
|
||||
tooltipifyLinks([root], [], containers);
|
||||
tooltipifyLinks([root], [], containers);
|
||||
tooltipifyLinks([root], [], containers);
|
||||
expect(containers).toHaveLength(1);
|
||||
expect(containers.elements).toHaveLength(1);
|
||||
const anchor = root.querySelector("a");
|
||||
expect(anchor?.getAttribute("href")).toEqual("/foo");
|
||||
const tooltip = anchor!.querySelector(".mx_TextWithTooltip_target");
|
||||
|
|
261
test/unit-tests/vector/__snapshots__/init-test.ts.snap
Normal file
261
test/unit-tests/vector/__snapshots__/init-test.ts.snap
Normal file
|
@ -0,0 +1,261 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`showError should match snapshot 1`] = `
|
||||
<div
|
||||
id="matrixchat"
|
||||
>
|
||||
<div
|
||||
class="mx_ErrorView cpd-theme-light"
|
||||
>
|
||||
<img
|
||||
alt="Element"
|
||||
class="mx_ErrorView_logo"
|
||||
height="160"
|
||||
src="themes/element/img/logos/element-app-logo.png"
|
||||
/>
|
||||
<div
|
||||
class="mx_ErrorView_container"
|
||||
>
|
||||
<h1
|
||||
class="_typography_yh5dq_162 _font-heading-md-semibold_yh5dq_121"
|
||||
>
|
||||
Error title
|
||||
</h1>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-lg-regular_yh5dq_78"
|
||||
>
|
||||
msg1
|
||||
</p>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-lg-regular_yh5dq_78"
|
||||
>
|
||||
msg2
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`showIncompatibleBrowser should match snapshot 1`] = `
|
||||
<div
|
||||
id="matrixchat"
|
||||
>
|
||||
<div
|
||||
class="mx_ErrorView cpd-theme-light"
|
||||
>
|
||||
<img
|
||||
alt="Element"
|
||||
class="mx_ErrorView_logo"
|
||||
height="160"
|
||||
src="themes/element/img/logos/element-app-logo.png"
|
||||
/>
|
||||
<div
|
||||
class="mx_ErrorView_container"
|
||||
>
|
||||
<h1
|
||||
class="_typography_yh5dq_162 _font-heading-md-semibold_yh5dq_121"
|
||||
>
|
||||
Element does not support this browser
|
||||
</h1>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-lg-regular_yh5dq_78"
|
||||
>
|
||||
Element uses some browser features which are not available in your current browser. If you continue, some features may stop working and there is a risk that you may lose data in the future.
|
||||
</p>
|
||||
<p
|
||||
class="_typography_yh5dq_162 _font-body-lg-regular_yh5dq_78"
|
||||
>
|
||||
<span>
|
||||
For the best experience, use
|
||||
<a
|
||||
href="https://google.com/chrome"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
Chrome
|
||||
</a>
|
||||
,
|
||||
<a
|
||||
href="https://firefox.com"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
Firefox
|
||||
</a>
|
||||
,
|
||||
<a
|
||||
href="https://microsoft.com/edge"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
Edge
|
||||
</a>
|
||||
, or
|
||||
<a
|
||||
href="https://apple.com/safari"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
Safari
|
||||
</a>
|
||||
.
|
||||
</span>
|
||||
</p>
|
||||
<div
|
||||
class="mx_Flex mx_ErrorView_buttons"
|
||||
>
|
||||
<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="M5 3h6a1 1 0 1 1 0 2H5v14h14v-6a1 1 0 1 1 2 0v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Z"
|
||||
/>
|
||||
<path
|
||||
d="M15 3h5a1 1 0 0 1 1 1v5a1 1 0 1 1-2 0V6.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L17.586 5H15a1 1 0 1 1 0-2Z"
|
||||
/>
|
||||
</svg>
|
||||
Learn more
|
||||
</button>
|
||||
<button
|
||||
class="_button_i91xf_17"
|
||||
data-kind="primary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Continue anyway
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="_separator_144s5_17"
|
||||
data-kind="primary"
|
||||
data-orientation="horizontal"
|
||||
role="separator"
|
||||
/>
|
||||
<h2
|
||||
class="_typography_yh5dq_162 _font-heading-sm-semibold_yh5dq_102"
|
||||
>
|
||||
Use Element Desktop instead
|
||||
</h2>
|
||||
<div
|
||||
class="mx_Flex"
|
||||
>
|
||||
<a
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="lg"
|
||||
href="https://packages.element.io/desktop/install/macos/Element.dmg"
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
height="20"
|
||||
width="20"
|
||||
/>
|
||||
Mac
|
||||
</a>
|
||||
<a
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="lg"
|
||||
href="https://packages.element.io/desktop/install/win32/x64/Element%20Setup.exe"
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
height="20"
|
||||
width="20"
|
||||
/>
|
||||
Windows (64-bit)
|
||||
</a>
|
||||
<a
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="lg"
|
||||
href="https://packages.element.io/desktop/install/win32/ia32/Element%20Setup.exe"
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
height="20"
|
||||
width="20"
|
||||
/>
|
||||
Windows (32-bit)
|
||||
</a>
|
||||
<a
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="lg"
|
||||
href="https://element.io/download#linux"
|
||||
role="link"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
height="20"
|
||||
width="20"
|
||||
/>
|
||||
Linux
|
||||
</a>
|
||||
</div>
|
||||
<h2
|
||||
class="_typography_yh5dq_162 _font-heading-sm-semibold_yh5dq_102"
|
||||
>
|
||||
Or use our mobile app
|
||||
</h2>
|
||||
<div
|
||||
class="mx_Flex"
|
||||
>
|
||||
<a
|
||||
href="https://apps.apple.com/app/vector/id1083446067"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
alt="Apple App Store"
|
||||
height="64"
|
||||
src="themes/element/img/download/apple.svg"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=im.vector.app"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
alt="Google Play Store"
|
||||
height="64"
|
||||
src="themes/element/img/download/google.svg"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://f-droid.org/repository/browse/?fdid=im.vector.app"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
alt="F-Droid"
|
||||
height="64"
|
||||
src="themes/element/img/download/fdroid.svg"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
33
test/unit-tests/vector/init-test.ts
Normal file
33
test/unit-tests/vector/init-test.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
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 { showError, showIncompatibleBrowser } from "../../../src/vector/init.tsx";
|
||||
|
||||
function setUpMatrixChatDiv() {
|
||||
document.getElementById("matrixchat")?.remove();
|
||||
const div = document.createElement("div");
|
||||
div.id = "matrixchat";
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
|
||||
describe("showIncompatibleBrowser", () => {
|
||||
beforeEach(setUpMatrixChatDiv);
|
||||
|
||||
it("should match snapshot", async () => {
|
||||
await showIncompatibleBrowser(jest.fn());
|
||||
expect(document.getElementById("matrixchat")).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("showError", () => {
|
||||
beforeEach(setUpMatrixChatDiv);
|
||||
|
||||
it("should match snapshot", async () => {
|
||||
await showError("Error title", ["msg1", "msg2"]);
|
||||
expect(document.getElementById("matrixchat")).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -6,7 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { getInitialScreenAfterLogin, onNewScreen } from "../../../src/vector/routing";
|
||||
import { getInitialScreenAfterLogin, init, onNewScreen } from "../../../src/vector/routing";
|
||||
import MatrixChat from "../../../src/components/structures/MatrixChat.tsx";
|
||||
|
||||
describe("onNewScreen", () => {
|
||||
it("should replace history if stripping via fields", () => {
|
||||
|
@ -95,3 +96,27 @@ describe("getInitialScreenAfterLogin", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("init", () => {
|
||||
afterAll(() => {
|
||||
// @ts-ignore
|
||||
delete window.matrixChat;
|
||||
});
|
||||
|
||||
it("should call showScreen on MatrixChat on hashchange", () => {
|
||||
Object.defineProperty(window, "location", {
|
||||
value: {
|
||||
hash: "#/room/!room:server?via=abc",
|
||||
},
|
||||
});
|
||||
|
||||
window.matrixChat = {
|
||||
showScreen: jest.fn(),
|
||||
} as unknown as MatrixChat;
|
||||
|
||||
init();
|
||||
window.dispatchEvent(new HashChangeEvent("hashchange"));
|
||||
|
||||
expect(window.matrixChat.showScreen).toHaveBeenCalledWith("room/!room:server", { via: "abc" });
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue