Simplify references to VerificationRequest
(#11045)
* Use new `VerificationRequest.getQRCodeBytes()` * Use new `VerificationRequest.otherDeviceId` * Remove references to `VerificationRequest.channel` * Replace references to `VerificationRequest.{requesting,receiving}UserId` Normally these are guarded by `request.initiatedByMe` so we can trivially replace it with `request.otherUserId` or `client.getUserId()`. In one place we actually need to apply some logic. * increase test coverage * Even more test coverage * Even more test coverage
This commit is contained in:
parent
ac2c9cef8d
commit
34439ee652
12 changed files with 310 additions and 33 deletions
|
@ -1667,7 +1667,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
);
|
);
|
||||||
} else if (request.pending) {
|
} else if (request.pending) {
|
||||||
ToastStore.sharedInstance().addOrReplaceToast({
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
key: "verifreq_" + request.channel.transactionId,
|
key: "verifreq_" + request.transactionId,
|
||||||
title: _t("Verification requested"),
|
title: _t("Verification requested"),
|
||||||
icon: "verification",
|
icon: "verification",
|
||||||
props: { request },
|
props: { request },
|
||||||
|
|
|
@ -15,19 +15,18 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { QRCodeData } from "matrix-js-sdk/src/crypto/verification/QRCode";
|
|
||||||
|
|
||||||
import QRCode from "../QRCode";
|
import QRCode from "../QRCode";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
qrCodeData: QRCodeData;
|
qrCodeBytes: Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class VerificationQRCode extends React.PureComponent<IProps> {
|
export default class VerificationQRCode extends React.PureComponent<IProps> {
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<QRCode
|
<QRCode
|
||||||
data={[{ data: this.props.qrCodeData.getBuffer(), mode: "byte" }]}
|
data={[{ data: this.props.qrCodeBytes, mode: "byte" }]}
|
||||||
className="mx_VerificationQRCode"
|
className="mx_VerificationQRCode"
|
||||||
width={196}
|
width={196}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -148,7 +148,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
if (accepted) {
|
if (accepted) {
|
||||||
stateLabel = (
|
stateLabel = (
|
||||||
<AccessibleButton onClick={this.openRequest}>
|
<AccessibleButton onClick={this.openRequest}>
|
||||||
{this.acceptedLabel(request.receivingUserId)}
|
{this.acceptedLabel(request.initiatedByMe ? request.otherUserId : client.getSafeUserId())}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
} else if (request.phase === VerificationPhase.Cancelled) {
|
} else if (request.phase === VerificationPhase.Cancelled) {
|
||||||
|
@ -162,9 +162,9 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!request.initiatedByMe) {
|
if (!request.initiatedByMe) {
|
||||||
const name = getNameForEventRoom(client, request.requestingUserId, mxEvent.getRoomId()!);
|
const name = getNameForEventRoom(client, request.otherUserId, mxEvent.getRoomId()!);
|
||||||
title = _t("%(name)s wants to verify", { name });
|
title = _t("%(name)s wants to verify", { name });
|
||||||
subtitle = userLabelForEventRoom(client, request.requestingUserId, mxEvent.getRoomId()!);
|
subtitle = userLabelForEventRoom(client, request.otherUserId, mxEvent.getRoomId()!);
|
||||||
if (request.canAccept) {
|
if (request.canAccept) {
|
||||||
stateNode = (
|
stateNode = (
|
||||||
<div className="mx_cryptoEvent_buttons">
|
<div className="mx_cryptoEvent_buttons">
|
||||||
|
@ -180,7 +180,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
|
||||||
} else {
|
} else {
|
||||||
// request sent by us
|
// request sent by us
|
||||||
title = _t("You sent a verification request");
|
title = _t("You sent a verification request");
|
||||||
subtitle = userLabelForEventRoom(client, request.receivingUserId, mxEvent.getRoomId()!);
|
subtitle = userLabelForEventRoom(client, request.otherUserId, mxEvent.getRoomId()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
|
|
|
@ -165,7 +165,7 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
member={member}
|
member={member}
|
||||||
request={request}
|
request={request}
|
||||||
key={request.channel.transactionId}
|
key={request.transactionId}
|
||||||
inDialog={layout === "dialog"}
|
inDialog={layout === "dialog"}
|
||||||
phase={phase}
|
phase={phase}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -67,6 +67,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
const { member, request } = this.props;
|
const { member, request } = this.props;
|
||||||
const showSAS: boolean = request.otherPartySupportsMethod(verificationMethods.SAS);
|
const showSAS: boolean = request.otherPartySupportsMethod(verificationMethods.SAS);
|
||||||
const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
||||||
|
const qrCodeBytes = showQR ? request.getQRCodeBytes() : null;
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
|
|
||||||
const noCommonMethodError: JSX.Element | null =
|
const noCommonMethodError: JSX.Element | null =
|
||||||
|
@ -85,11 +86,11 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
// HACK: This is a terrible idea.
|
// HACK: This is a terrible idea.
|
||||||
let qrBlockDialog: JSX.Element | undefined;
|
let qrBlockDialog: JSX.Element | undefined;
|
||||||
let sasBlockDialog: JSX.Element | undefined;
|
let sasBlockDialog: JSX.Element | undefined;
|
||||||
if (showQR && request.qrCodeData) {
|
if (!!qrCodeBytes) {
|
||||||
qrBlockDialog = (
|
qrBlockDialog = (
|
||||||
<div className="mx_VerificationPanel_QRPhase_startOption">
|
<div className="mx_VerificationPanel_QRPhase_startOption">
|
||||||
<p>{_t("Scan this unique code")}</p>
|
<p>{_t("Scan this unique code")}</p>
|
||||||
<VerificationQRCode qrCodeData={request.qrCodeData} />
|
<VerificationQRCode qrCodeBytes={qrCodeBytes} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -133,7 +134,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
}
|
}
|
||||||
|
|
||||||
let qrBlock: JSX.Element | undefined;
|
let qrBlock: JSX.Element | undefined;
|
||||||
if (showQR && request.qrCodeData) {
|
if (!!qrCodeBytes) {
|
||||||
qrBlock = (
|
qrBlock = (
|
||||||
<div className="mx_UserInfo_container">
|
<div className="mx_UserInfo_container">
|
||||||
<h3>{_t("Verify by scanning")}</h3>
|
<h3>{_t("Verify by scanning")}</h3>
|
||||||
|
@ -144,7 +145,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="mx_VerificationPanel_qrCode">
|
<div className="mx_VerificationPanel_qrCode">
|
||||||
<VerificationQRCode qrCodeData={request.qrCodeData} />
|
<VerificationQRCode qrCodeBytes={qrCodeBytes} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -201,7 +202,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
};
|
};
|
||||||
|
|
||||||
private getDevice(): DeviceInfo | null {
|
private getDevice(): DeviceInfo | null {
|
||||||
const deviceId = this.props.request && this.props.request.channel.deviceId;
|
const deviceId = this.props.request && this.props.request.otherDeviceId;
|
||||||
const userId = MatrixClientPeg.get().getUserId();
|
const userId = MatrixClientPeg.get().getUserId();
|
||||||
if (deviceId && userId) {
|
if (deviceId && userId) {
|
||||||
return MatrixClientPeg.get().getStoredDevice(userId, deviceId);
|
return MatrixClientPeg.get().getStoredDevice(userId, deviceId);
|
||||||
|
@ -275,12 +276,12 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
if (!device) {
|
if (!device) {
|
||||||
// This can happen if the device is logged out while we're still showing verification
|
// This can happen if the device is logged out while we're still showing verification
|
||||||
// UI for it.
|
// UI for it.
|
||||||
logger.warn("Verified device we don't know about: " + this.props.request.channel.deviceId);
|
logger.warn("Verified device we don't know about: " + this.props.request.otherDeviceId);
|
||||||
description = _t("You've successfully verified your device!");
|
description = _t("You've successfully verified your device!");
|
||||||
} else {
|
} else {
|
||||||
description = _t("You've successfully verified %(deviceName)s (%(deviceId)s)!", {
|
description = _t("You've successfully verified %(deviceName)s (%(deviceId)s)!", {
|
||||||
deviceName: device ? device.getDisplayName() : "",
|
deviceName: device ? device.getDisplayName() : "",
|
||||||
deviceId: this.props.request.channel.deviceId,
|
deviceId: this.props.request.otherDeviceId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -74,11 +74,11 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||||
|
|
||||||
if (request.isSelfVerification) {
|
if (request.isSelfVerification) {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const device = request.channel.deviceId ? await cli.getDevice(request.channel.deviceId) : null;
|
const device = request.otherDeviceId ? await cli.getDevice(request.otherDeviceId) : null;
|
||||||
const ip = device?.last_seen_ip;
|
const ip = device?.last_seen_ip;
|
||||||
this.setState({
|
this.setState({
|
||||||
device:
|
device:
|
||||||
(request.channel.deviceId && cli.getStoredDevice(cli.getSafeUserId(), request.channel.deviceId)) ||
|
(request.otherDeviceId && cli.getStoredDevice(cli.getSafeUserId(), request.otherDeviceId)) ||
|
||||||
undefined,
|
undefined,
|
||||||
ip,
|
ip,
|
||||||
});
|
});
|
||||||
|
@ -113,10 +113,10 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||||
// no room id for to_device requests
|
// no room id for to_device requests
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
try {
|
try {
|
||||||
if (request.channel.roomId) {
|
if (request.roomId) {
|
||||||
dis.dispatch<ViewRoomPayload>({
|
dis.dispatch<ViewRoomPayload>({
|
||||||
action: Action.ViewRoom,
|
action: Action.ViewRoom,
|
||||||
room_id: request.channel.roomId,
|
room_id: request.roomId,
|
||||||
should_peek: false,
|
should_peek: false,
|
||||||
metricsTrigger: "VerificationRequest",
|
metricsTrigger: "VerificationRequest",
|
||||||
});
|
});
|
||||||
|
@ -128,7 +128,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||||
{ phase: RightPanelPhases.EncryptionPanel, state: { verificationRequest: request, member } },
|
{ phase: RightPanelPhases.EncryptionPanel, state: { verificationRequest: request, member } },
|
||||||
],
|
],
|
||||||
undefined,
|
undefined,
|
||||||
request.channel.roomId,
|
request.roomId,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Modal.createDialog(
|
Modal.createDialog(
|
||||||
|
@ -164,7 +164,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const userId = request.otherUserId;
|
const userId = request.otherUserId;
|
||||||
const roomId = request.channel.roomId;
|
const roomId = request.roomId;
|
||||||
description = roomId ? userLabelForEventRoom(MatrixClientPeg.get(), userId, roomId) : userId;
|
description = roomId ? userLabelForEventRoom(MatrixClientPeg.get(), userId, roomId) : userId;
|
||||||
// for legacy to_device verification requests
|
// for legacy to_device verification requests
|
||||||
if (description === userId) {
|
if (description === userId) {
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { cleanup, render, waitFor } from "@testing-library/react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import VerificationQRCode from "../../../../../src/components/views/elements/crypto/VerificationQRCode";
|
||||||
|
|
||||||
|
describe("<VerificationQRCode />", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders a QR code", async () => {
|
||||||
|
const { container, getAllByAltText } = render(<VerificationQRCode qrCodeBytes={Buffer.from("asd")} />);
|
||||||
|
// wait for the spinner to go away
|
||||||
|
await waitFor(() => getAllByAltText("QR Code").length === 1);
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<VerificationQRCode /> renders a QR code 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="mx_QRCode mx_VerificationQRCode"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="QR Code"
|
||||||
|
class="mx_VerificationQRCode"
|
||||||
|
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAYAAADApo5rAAAAAklEQVR4AewaftIAAAPVSURBVO3BQQ4bRxAEwawG///lsiADfdqBQWpFkVZGpD8g6adB0hokrUHSGiStQdIaJK1B0hokrUHSGiStQdIaJK1B0hokrUHSGiStQdIaJK1B0hokrUHSGiStQdIaJK1B0hokrUHSevBmSfhWbXlWEk7acpKEZ7XlJAnfqi3vMkhag6Q1SFqDpDVIWg8+RFs+QRLeJQmvaMud2vIJkvCnDZLWIGkNktYgaQ2S1iBpPfgCSbhTW+6WhGe15SQJJ0m40pa7JeFObflkg6Q1SFqDpDVIWoOkNUhaD3SLtlxJwt3aot9jkLQGSWuQtAZJa5C0BknrgX6rtpwk4aQtJ0m40hb9mkHSGiStQdIaJK1B0nrwBdryf9SWT9eWv8kgaQ2S1iBpDZLWIGkNktaDD5GEb5aEK205ScJJW94lCfrXIGkNktYgaQ2S1iBpDZLWgzdry98kCSdtOUnCndqi/zZIWoOkNUhag6Q1SFqDpPXgzZJwp7acJOGTJeEVbbmShFe05U5JOGnLJxskrUHSGiStQdIaJK0Hb9aWK0k4actJEk7aciUJJ235Vm05ScKdknDSllck4Upb3mWQtAZJa5C0BklrkLQGSevBmyXhSlte0ZY7JeEVbbmShJO2vCIJV9py0pZXJOFOSfhkg6Q1SFqDpDVIWoOkNUhaD/7HknClLZ8gCSdtOWnLs5Jw0paTttypLZ9skLQGSWuQtAZJa5C0BknrwRdIwrsk4aQtd2rLK5JwpS13S8KVttwtCVfa8i6DpDVIWoOkNUhag6SV/oB+WRLepS1XknDSlpMknLTlTkl4VlveZZC0BklrkLQGSWuQtAZJ68GbJeFbteVZbTlJwrsk4V2S8K0GSWuQtAZJa5C0BklrkLQefIi2fIIkvEtbTpJwkoRnteUkCSdJeFZbXpGEP22QtAZJa5C0BklrkLQGSevBF0jCndryzdpyJQknSXhFW56VhJO2fLJB0hokrUHSGiStQdJ6oFu05UoSTtpy0pZnteUkCSdteZcknLTlTxskrUHSGiStQdIaJK1B0nqgWyThWUk4actJEq605aQtr0jClbactOUVSbjSlncZJK1B0hokrUHSGiStQdJ68AXa8q3acpKEV7TlWUk4actJW56VhFe05U8bJK1B0hokrUHSGiStQdJ68CGS8LdpyyuScKUtd0vClbbcLQlX2vIug6Q1SFqDpDVIWoOklf6ApJ8GSWuQtAZJa5C0BklrkLQGSWuQtAZJa5C0BklrkLQGSWuQtAZJa5C0BklrkLQGSWuQtAZJa5C0BklrkLQGSWuQtP4BohkVlic75PUAAAAASUVORK5CYII="
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { render } from "@testing-library/react";
|
import { render, within } from "@testing-library/react";
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import {
|
import {
|
||||||
|
@ -74,6 +74,29 @@ describe("MKeyVerificationRequest", () => {
|
||||||
expect(container).toHaveTextContent("You sent a verification request");
|
expect(container).toHaveTextContent("You sent a verification request");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render appropriately when the request was initiated by me and has been accepted", () => {
|
||||||
|
const event = new MatrixEvent({ type: "m.key.verification.request" });
|
||||||
|
event.verificationRequest = getMockVerificationRequest({
|
||||||
|
phase: VerificationPhase.Ready,
|
||||||
|
otherUserId: "@other:user",
|
||||||
|
});
|
||||||
|
const { container } = render(<MKeyVerificationRequest mxEvent={event} />);
|
||||||
|
expect(container).toHaveTextContent("You sent a verification request");
|
||||||
|
expect(within(container).getByRole("button")).toHaveTextContent("@other:user accepted");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render appropriately when the request was initiated by the other user and has been accepted", () => {
|
||||||
|
const event = new MatrixEvent({ type: "m.key.verification.request" });
|
||||||
|
event.verificationRequest = getMockVerificationRequest({
|
||||||
|
phase: VerificationPhase.Ready,
|
||||||
|
initiatedByMe: false,
|
||||||
|
otherUserId: "@other:user",
|
||||||
|
});
|
||||||
|
const { container } = render(<MKeyVerificationRequest mxEvent={event} />);
|
||||||
|
expect(container).toHaveTextContent("@other:user wants to verify");
|
||||||
|
expect(within(container).getByRole("button")).toHaveTextContent("You accepted");
|
||||||
|
});
|
||||||
|
|
||||||
it("should render appropriately when the request was cancelled", () => {
|
it("should render appropriately when the request was cancelled", () => {
|
||||||
const event = new MatrixEvent({ type: "m.key.verification.request" });
|
const event = new MatrixEvent({ type: "m.key.verification.request" });
|
||||||
event.verificationRequest = getMockVerificationRequest({
|
event.verificationRequest = getMockVerificationRequest({
|
||||||
|
|
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { act, render } from "@testing-library/react";
|
import { act, render, waitFor } from "@testing-library/react";
|
||||||
import React from "react";
|
import React, { ComponentProps } from "react";
|
||||||
import {
|
import {
|
||||||
Phase,
|
Phase,
|
||||||
VerificationRequest,
|
VerificationRequest,
|
||||||
|
@ -31,7 +31,6 @@ import {
|
||||||
VerifierEvent,
|
VerifierEvent,
|
||||||
VerifierEventHandlerMap,
|
VerifierEventHandlerMap,
|
||||||
} from "matrix-js-sdk/src/crypto-api/verification";
|
} from "matrix-js-sdk/src/crypto-api/verification";
|
||||||
import { IVerificationChannel } from "matrix-js-sdk/src/crypto/verification/request/Channel";
|
|
||||||
|
|
||||||
import VerificationPanel from "../../../../src/components/views/right_panel/VerificationPanel";
|
import VerificationPanel from "../../../../src/components/views/right_panel/VerificationPanel";
|
||||||
import { stubClient } from "../../../test-utils";
|
import { stubClient } from "../../../test-utils";
|
||||||
|
@ -41,14 +40,59 @@ describe("<VerificationPanel />", () => {
|
||||||
stubClient();
|
stubClient();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show a 'Verify by emoji' button", () => {
|
describe("'Ready' phase (dialog mode)", () => {
|
||||||
|
it("should show a 'Start' button", () => {
|
||||||
const container = renderComponent({
|
const container = renderComponent({
|
||||||
request: makeMockVerificationRequest(),
|
request: makeMockVerificationRequest({
|
||||||
|
phase: Phase.Ready,
|
||||||
|
}),
|
||||||
|
layout: "dialog",
|
||||||
|
});
|
||||||
|
container.getByRole("button", { name: "Start" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show a QR code if the other side can scan and QR bytes are calculated", async () => {
|
||||||
|
const request = makeMockVerificationRequest({
|
||||||
phase: Phase.Ready,
|
phase: Phase.Ready,
|
||||||
});
|
});
|
||||||
|
request.getQRCodeBytes.mockReturnValue(Buffer.from("test", "utf-8"));
|
||||||
|
const container = renderComponent({
|
||||||
|
request: request,
|
||||||
|
layout: "dialog",
|
||||||
|
});
|
||||||
|
container.getByText("Scan this unique code");
|
||||||
|
// it shows a spinner at first; wait for the update which makes it show the QR code
|
||||||
|
await waitFor(() => {
|
||||||
|
container.getByAltText("QR Code");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("'Ready' phase (regular mode)", () => {
|
||||||
|
it("should show a 'Verify by emoji' button", () => {
|
||||||
|
const container = renderComponent({
|
||||||
|
request: makeMockVerificationRequest({ phase: Phase.Ready }),
|
||||||
|
});
|
||||||
container.getByRole("button", { name: "Verify by emoji" });
|
container.getByRole("button", { name: "Verify by emoji" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should show a QR code if the other side can scan and QR bytes are calculated", async () => {
|
||||||
|
const request = makeMockVerificationRequest({
|
||||||
|
phase: Phase.Ready,
|
||||||
|
});
|
||||||
|
request.getQRCodeBytes.mockReturnValue(Buffer.from("test", "utf-8"));
|
||||||
|
const container = renderComponent({
|
||||||
|
request: request,
|
||||||
|
member: new User("@other:user"),
|
||||||
|
});
|
||||||
|
container.getByText("Ask @other:user to scan your code:");
|
||||||
|
// it shows a spinner at first; wait for the update which makes it show the QR code
|
||||||
|
await waitFor(() => {
|
||||||
|
container.getByAltText("QR Code");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("'Verify by emoji' flow", () => {
|
describe("'Verify by emoji' flow", () => {
|
||||||
let mockVerifier: Mocked<VerificationBase>;
|
let mockVerifier: Mocked<VerificationBase>;
|
||||||
let mockRequest: Mocked<VerificationRequest>;
|
let mockRequest: Mocked<VerificationRequest>;
|
||||||
|
@ -91,13 +135,14 @@ describe("<VerificationPanel />", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function renderComponent(props: { request: VerificationRequest; phase: Phase }) {
|
function renderComponent(props: Partial<ComponentProps<typeof VerificationPanel>> & { request: VerificationRequest }) {
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
layout: "",
|
layout: "",
|
||||||
member: {} as User,
|
member: {} as User,
|
||||||
onClose: () => undefined,
|
onClose: () => undefined,
|
||||||
isRoomEncrypted: false,
|
isRoomEncrypted: false,
|
||||||
inDialog: false,
|
inDialog: false,
|
||||||
|
phase: props.request.phase,
|
||||||
};
|
};
|
||||||
return render(<VerificationPanel {...defaultProps} {...props} />);
|
return render(<VerificationPanel {...defaultProps} {...props} />);
|
||||||
}
|
}
|
||||||
|
@ -105,9 +150,9 @@ function renderComponent(props: { request: VerificationRequest; phase: Phase })
|
||||||
function makeMockVerificationRequest(props: Partial<VerificationRequest> = {}): Mocked<VerificationRequest> {
|
function makeMockVerificationRequest(props: Partial<VerificationRequest> = {}): Mocked<VerificationRequest> {
|
||||||
const request = new TypedEventEmitter<VerificationRequestEvent, any>();
|
const request = new TypedEventEmitter<VerificationRequestEvent, any>();
|
||||||
Object.assign(request, {
|
Object.assign(request, {
|
||||||
channel: {} as IVerificationChannel,
|
|
||||||
cancel: jest.fn(),
|
cancel: jest.fn(),
|
||||||
otherPartySupportsMethod: jest.fn().mockReturnValue(true),
|
otherPartySupportsMethod: jest.fn().mockReturnValue(true),
|
||||||
|
getQRCodeBytes: jest.fn(),
|
||||||
...props,
|
...props,
|
||||||
});
|
});
|
||||||
return request as unknown as Mocked<VerificationRequest>;
|
return request as unknown as Mocked<VerificationRequest>;
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { ComponentProps } from "react";
|
||||||
|
import { Mocked } from "jest-mock";
|
||||||
|
import { act, render, RenderResult } from "@testing-library/react";
|
||||||
|
import {
|
||||||
|
VerificationRequest,
|
||||||
|
VerificationRequestEvent,
|
||||||
|
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
||||||
|
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||||
|
import { IMyDevice, MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
|
import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter";
|
||||||
|
|
||||||
|
import VerificationRequestToast from "../../../../src/components/views/toasts/VerificationRequestToast";
|
||||||
|
import { flushPromises, getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../test-utils";
|
||||||
|
|
||||||
|
function renderComponent(
|
||||||
|
props: Partial<ComponentProps<typeof VerificationRequestToast>> & { request: VerificationRequest },
|
||||||
|
): RenderResult {
|
||||||
|
const propsWithDefaults = {
|
||||||
|
toastKey: "test",
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
|
||||||
|
return render(<VerificationRequestToast {...propsWithDefaults} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("VerificationRequestToast", () => {
|
||||||
|
let client: Mocked<MatrixClient>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
client = getMockClientWithEventEmitter({
|
||||||
|
...mockClientMethodsUser(),
|
||||||
|
getStoredDevice: jest.fn(),
|
||||||
|
getDevice: jest.fn(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render a self-verification", async () => {
|
||||||
|
const otherDeviceId = "other_device";
|
||||||
|
const otherIDevice: IMyDevice = { device_id: otherDeviceId, last_seen_ip: "1.1.1.1" };
|
||||||
|
client.getDevice.mockResolvedValue(otherIDevice);
|
||||||
|
|
||||||
|
const otherDeviceInfo = new DeviceInfo(otherDeviceId);
|
||||||
|
otherDeviceInfo.unsigned = { device_display_name: "my other device" };
|
||||||
|
client.getStoredDevice.mockReturnValue(otherDeviceInfo);
|
||||||
|
|
||||||
|
const request = makeMockVerificationRequest({
|
||||||
|
isSelfVerification: true,
|
||||||
|
otherDeviceId,
|
||||||
|
});
|
||||||
|
const result = renderComponent({ request });
|
||||||
|
await act(async () => {
|
||||||
|
await flushPromises();
|
||||||
|
});
|
||||||
|
expect(result.container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render a cross-user verification", async () => {
|
||||||
|
const otherUserId = "@other:user";
|
||||||
|
const request = makeMockVerificationRequest({
|
||||||
|
isSelfVerification: false,
|
||||||
|
otherUserId,
|
||||||
|
});
|
||||||
|
const result = renderComponent({ request });
|
||||||
|
await act(async () => {
|
||||||
|
await flushPromises();
|
||||||
|
});
|
||||||
|
expect(result.container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeMockVerificationRequest(props: Partial<VerificationRequest> = {}): Mocked<VerificationRequest> {
|
||||||
|
const request = new TypedEventEmitter<VerificationRequestEvent, any>();
|
||||||
|
Object.assign(request, {
|
||||||
|
...props,
|
||||||
|
});
|
||||||
|
return request as unknown as Mocked<VerificationRequest>;
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`VerificationRequestToast should render a cross-user verification 1`] = `
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="mx_Toast_description"
|
||||||
|
>
|
||||||
|
@alice:domain (@other:user)
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-live="off"
|
||||||
|
class="mx_Toast_buttons"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger_outline"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Ignore (NaN)
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Verify Session
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`VerificationRequestToast should render a self-verification 1`] = `
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="mx_Toast_description"
|
||||||
|
>
|
||||||
|
my other device
|
||||||
|
<div
|
||||||
|
class="mx_Toast_detail"
|
||||||
|
>
|
||||||
|
other_device from 1.1.1.1
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-live="off"
|
||||||
|
class="mx_Toast_buttons"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger_outline"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Ignore (NaN)
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Verify Session
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
Loading…
Reference in a new issue