Removed DecryptionFailureBar.tsx
(#11027)
This commit is contained in:
parent
6b46d6e4f8
commit
68ebcd2956
6 changed files with 0 additions and 1739 deletions
|
@ -1,277 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2022 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 type { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
|
|
||||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
|
||||||
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
|
||||||
import { UserCredentials } from "../../support/login";
|
|
||||||
import { handleVerificationRequest } from "./utils";
|
|
||||||
import { skipIfRustCrypto } from "../../support/util";
|
|
||||||
|
|
||||||
const ROOM_NAME = "Test room";
|
|
||||||
const TEST_USER = "Alia";
|
|
||||||
const BOT_USER = "Benjamin";
|
|
||||||
|
|
||||||
type EmojiMapping = [emoji: string, name: string];
|
|
||||||
|
|
||||||
const waitForVerificationRequest = (cli: MatrixClient): Promise<VerificationRequest> => {
|
|
||||||
return new Promise<VerificationRequest>((resolve) => {
|
|
||||||
const onVerificationRequestEvent = (request: VerificationRequest) => {
|
|
||||||
// @ts-ignore CryptoEvent is not exported to window.matrixcs; using the string value here
|
|
||||||
cli.off("crypto.verification.request", onVerificationRequestEvent);
|
|
||||||
resolve(request);
|
|
||||||
};
|
|
||||||
// @ts-ignore
|
|
||||||
cli.on("crypto.verification.request", onVerificationRequestEvent);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkTimelineNarrow = (button = true) => {
|
|
||||||
cy.viewport(800, 600); // SVGA
|
|
||||||
cy.get(".mx_LeftPanel_minimized").should("exist"); // Wait until the left panel is minimized
|
|
||||||
cy.findByRole("button", { name: "Room info" }).click(); // Open the right panel to make the timeline narrow
|
|
||||||
cy.get(".mx_BaseCard").should("exist");
|
|
||||||
|
|
||||||
// Ensure the failure bar does not cover the timeline
|
|
||||||
cy.get(".mx_RoomView_body .mx_EventTile.mx_EventTile_last").should("be.visible");
|
|
||||||
|
|
||||||
// Ensure the indicator does not overflow the timeline
|
|
||||||
cy.findByTestId("decryption-failure-bar-indicator").should("be.visible");
|
|
||||||
|
|
||||||
if (button) {
|
|
||||||
// Ensure the button does not overflow the timeline
|
|
||||||
cy.get("[data-testid='decryption-failure-bar-button']:last-of-type").should("be.visible");
|
|
||||||
}
|
|
||||||
|
|
||||||
cy.findByRole("button", { name: "Room info" }).click(); // Close the right panel
|
|
||||||
cy.get(".mx_BaseCard").should("not.exist");
|
|
||||||
cy.viewport(1000, 660); // Reset to the default size
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("Decryption Failure Bar", () => {
|
|
||||||
let homeserver: HomeserverInstance | undefined;
|
|
||||||
let testUser: UserCredentials | undefined;
|
|
||||||
let bot: MatrixClient | undefined;
|
|
||||||
let roomId: string;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
skipIfRustCrypto();
|
|
||||||
cy.startHomeserver("default").then((hs: HomeserverInstance) => {
|
|
||||||
homeserver = hs;
|
|
||||||
cy.initTestUser(homeserver, TEST_USER)
|
|
||||||
.then((creds: UserCredentials) => {
|
|
||||||
testUser = creds;
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
cy.getBot(homeserver, { displayName: BOT_USER }).then((cli) => {
|
|
||||||
bot = cli;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
cy.createRoom({ name: ROOM_NAME }).then((id) => {
|
|
||||||
roomId = id;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
cy.inviteUser(roomId, bot.getUserId());
|
|
||||||
cy.visit("/#/room/" + roomId);
|
|
||||||
cy.findByText(BOT_USER + " joined the room").should("exist");
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
cy.getClient()
|
|
||||||
.then(async (cli) => {
|
|
||||||
await cli.setRoomEncryption(roomId, { algorithm: "m.megolm.v1.aes-sha2" });
|
|
||||||
await bot.setRoomEncryption(roomId, { algorithm: "m.megolm.v1.aes-sha2" });
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
bot.getRoom(roomId).setBlacklistUnverifiedDevices(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
cy.stopHomeserver(homeserver);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(
|
|
||||||
"should prompt the user to verify, if this device isn't verified " +
|
|
||||||
"and there are other verified devices or backups",
|
|
||||||
() => {
|
|
||||||
let otherDevice: MatrixClient | undefined;
|
|
||||||
cy.loginBot(homeserver, testUser.username, testUser.password, { bootstrapCrossSigning: true })
|
|
||||||
.then(async (cli) => {
|
|
||||||
otherDevice = cli;
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
cy.botSendMessage(bot, roomId, "test");
|
|
||||||
cy.get(".mx_DecryptionFailureBar_start_headline").within(() => {
|
|
||||||
cy.findByText("Decrypting messages…").should("be.visible");
|
|
||||||
cy.findByText("Verify this device to access all messages").should("be.visible");
|
|
||||||
});
|
|
||||||
|
|
||||||
checkTimelineNarrow();
|
|
||||||
|
|
||||||
cy.get(".mx_DecryptionFailureBar").percySnapshotElement(
|
|
||||||
"DecryptionFailureBar prompts user to verify",
|
|
||||||
{
|
|
||||||
widths: [320, 640],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.get(".mx_DecryptionFailureBar_end").within(() => {
|
|
||||||
cy.findByText("Resend key requests").should("not.exist");
|
|
||||||
cy.findByRole("button", { name: "Verify" }).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
const verificationRequestPromise = waitForVerificationRequest(otherDevice);
|
|
||||||
cy.findByRole("button", { name: "Verify with another device" }).click();
|
|
||||||
cy.findByText("To proceed, please accept the verification request on your other device.").should(
|
|
||||||
"be.visible",
|
|
||||||
);
|
|
||||||
cy.wrap(verificationRequestPromise).then((verificationRequest: VerificationRequest) => {
|
|
||||||
cy.wrap(verificationRequest.accept());
|
|
||||||
cy.wrap(
|
|
||||||
handleVerificationRequest(verificationRequest),
|
|
||||||
// extra timeout, as this sometimes takes a while
|
|
||||||
{ timeout: 30_000 },
|
|
||||||
).then((emojis: EmojiMapping[]) => {
|
|
||||||
cy.get(".mx_VerificationShowSas_emojiSas_block").then((emojiBlocks) => {
|
|
||||||
emojis.forEach((emoji: EmojiMapping, index: number) => {
|
|
||||||
expect(emojiBlocks[index].textContent.toLowerCase()).to.eq(emoji[0] + emoji[1]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
cy.findByRole("button", { name: "They match" }).click();
|
|
||||||
cy.get(".mx_VerificationPanel_verified_section .mx_E2EIcon_verified").should("exist");
|
|
||||||
cy.findByRole("button", { name: "Got it" }).click();
|
|
||||||
|
|
||||||
cy.get(".mx_DecryptionFailureBar_start_headline").within(() => {
|
|
||||||
cy.findByText("Open another device to load encrypted messages").should("be.visible");
|
|
||||||
});
|
|
||||||
|
|
||||||
checkTimelineNarrow();
|
|
||||||
|
|
||||||
cy.get(".mx_DecryptionFailureBar").percySnapshotElement(
|
|
||||||
"DecryptionFailureBar prompts user to open another device, with Resend Key Requests button",
|
|
||||||
{
|
|
||||||
widths: [320, 640],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.intercept("/_matrix/client/r0/sendToDevice/m.room_key_request/*").as("keyRequest");
|
|
||||||
cy.findByRole("button", { name: "Resend key requests" }).click();
|
|
||||||
cy.wait("@keyRequest");
|
|
||||||
cy.get(".mx_DecryptionFailureBar_end").within(() => {
|
|
||||||
cy.findByText("Resend key requests").should("not.exist");
|
|
||||||
cy.findByRole("button", { name: "View your device list" }).should("be.visible");
|
|
||||||
});
|
|
||||||
|
|
||||||
checkTimelineNarrow();
|
|
||||||
|
|
||||||
cy.get(".mx_DecryptionFailureBar").percySnapshotElement(
|
|
||||||
"DecryptionFailureBar prompts user to open another device, without Resend Key Requests button",
|
|
||||||
{
|
|
||||||
widths: [320, 640],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
|
||||||
"should prompt the user to reset keys, if this device isn't verified " +
|
|
||||||
"and there are no other verified devices or backups",
|
|
||||||
() => {
|
|
||||||
cy.loginBot(homeserver, testUser.username, testUser.password, { bootstrapCrossSigning: true }).then(
|
|
||||||
async (cli) => {
|
|
||||||
await cli.logout(true);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.botSendMessage(bot, roomId, "test");
|
|
||||||
cy.get(".mx_DecryptionFailureBar_start_headline").within(() => {
|
|
||||||
cy.findByText("Reset your keys to prevent future decryption errors").should("be.visible");
|
|
||||||
});
|
|
||||||
|
|
||||||
checkTimelineNarrow();
|
|
||||||
|
|
||||||
cy.get(".mx_DecryptionFailureBar").percySnapshotElement("DecryptionFailureBar prompts user to reset keys", {
|
|
||||||
widths: [320, 640],
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.findByRole("button", { name: "Reset" }).click();
|
|
||||||
|
|
||||||
// Set up key backup
|
|
||||||
cy.get(".mx_Dialog").within(() => {
|
|
||||||
cy.findByRole("button", { name: "Continue" }).click();
|
|
||||||
cy.get(".mx_CreateSecretStorageDialog_recoveryKey code").invoke("text").as("securityKey");
|
|
||||||
// Clicking download instead of Copy because of https://github.com/cypress-io/cypress/issues/2851
|
|
||||||
cy.findByRole("button", { name: "Download" }).click();
|
|
||||||
cy.get(".mx_Dialog_primary:not([disabled])").should("have.length", 3);
|
|
||||||
cy.findByRole("button", { name: "Continue" }).click();
|
|
||||||
cy.findByRole("button", { name: "Done" }).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(".mx_DecryptionFailureBar_start_headline").within(() => {
|
|
||||||
cy.findByText("Some messages could not be decrypted").should("be.visible");
|
|
||||||
});
|
|
||||||
|
|
||||||
checkTimelineNarrow(false); // button should not be rendered here
|
|
||||||
|
|
||||||
cy.get(".mx_DecryptionFailureBar").percySnapshotElement(
|
|
||||||
"DecryptionFailureBar displays general message with no call to action",
|
|
||||||
{
|
|
||||||
widths: [320, 640],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
it("should appear and disappear as undecryptable messages enter and leave view", () => {
|
|
||||||
cy.getClient().then((cli) => {
|
|
||||||
for (let i = 0; i < 25; i++) {
|
|
||||||
cy.botSendMessage(cli, roomId, `test ${i}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cy.botSendMessage(bot, roomId, "test");
|
|
||||||
cy.get(".mx_DecryptionFailureBar").should("exist");
|
|
||||||
cy.get(".mx_DecryptionFailureBar .mx_Spinner").should("exist");
|
|
||||||
|
|
||||||
cy.get(".mx_DecryptionFailureBar").percySnapshotElement("DecryptionFailureBar displays loading spinner", {
|
|
||||||
allowSpinners: true,
|
|
||||||
widths: [320, 640],
|
|
||||||
});
|
|
||||||
|
|
||||||
checkTimelineNarrow();
|
|
||||||
|
|
||||||
cy.wait(5000);
|
|
||||||
cy.get(".mx_DecryptionFailureBar .mx_Spinner").should("not.exist");
|
|
||||||
cy.findByTestId("decryption-failure-bar-icon").should("be.visible");
|
|
||||||
|
|
||||||
cy.get(".mx_RoomView_messagePanel").scrollTo("top");
|
|
||||||
cy.get(".mx_DecryptionFailureBar").should("not.exist");
|
|
||||||
|
|
||||||
cy.botSendMessage(bot, roomId, "another test");
|
|
||||||
cy.get(".mx_DecryptionFailureBar").should("not.exist");
|
|
||||||
|
|
||||||
cy.get(".mx_RoomView_messagePanel").scrollTo("bottom");
|
|
||||||
cy.get(".mx_DecryptionFailureBar").should("exist");
|
|
||||||
|
|
||||||
checkTimelineNarrow();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -62,7 +62,6 @@ import RoomPreviewBar from "../views/rooms/RoomPreviewBar";
|
||||||
import RoomPreviewCard from "../views/rooms/RoomPreviewCard";
|
import RoomPreviewCard from "../views/rooms/RoomPreviewCard";
|
||||||
import SearchBar, { SearchScope } from "../views/rooms/SearchBar";
|
import SearchBar, { SearchScope } from "../views/rooms/SearchBar";
|
||||||
import RoomUpgradeWarningBar from "../views/rooms/RoomUpgradeWarningBar";
|
import RoomUpgradeWarningBar from "../views/rooms/RoomUpgradeWarningBar";
|
||||||
import { DecryptionFailureBar } from "../views/rooms/DecryptionFailureBar";
|
|
||||||
import AuxPanel from "../views/rooms/AuxPanel";
|
import AuxPanel from "../views/rooms/AuxPanel";
|
||||||
import RoomHeader, { ISearchInfo } from "../views/rooms/RoomHeader";
|
import RoomHeader, { ISearchInfo } from "../views/rooms/RoomHeader";
|
||||||
import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
|
import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
|
||||||
|
@ -227,8 +226,6 @@ export interface IRoomState {
|
||||||
threadId?: string;
|
threadId?: string;
|
||||||
liveTimeline?: EventTimeline;
|
liveTimeline?: EventTimeline;
|
||||||
narrow: boolean;
|
narrow: boolean;
|
||||||
// List of undecryptable events currently visible on-screen
|
|
||||||
visibleDecryptionFailures?: MatrixEvent[];
|
|
||||||
msc3946ProcessDynamicPredecessor: boolean;
|
msc3946ProcessDynamicPredecessor: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +425,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
timelineRenderingType: TimelineRenderingType.Room,
|
timelineRenderingType: TimelineRenderingType.Room,
|
||||||
liveTimeline: undefined,
|
liveTimeline: undefined,
|
||||||
narrow: false,
|
narrow: false,
|
||||||
visibleDecryptionFailures: [],
|
|
||||||
msc3946ProcessDynamicPredecessor: SettingsStore.getValue("feature_dynamic_room_predecessors"),
|
msc3946ProcessDynamicPredecessor: SettingsStore.getValue("feature_dynamic_room_predecessors"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1244,7 +1240,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
private onEventDecrypted = (ev: MatrixEvent): void => {
|
private onEventDecrypted = (ev: MatrixEvent): void => {
|
||||||
if (!this.state.room || !this.state.matrixClientIsReady) return; // not ready at all
|
if (!this.state.room || !this.state.matrixClientIsReady) return; // not ready at all
|
||||||
if (ev.getRoomId() !== this.state.room.roomId) return; // not for us
|
if (ev.getRoomId() !== this.state.room.roomId) return; // not for us
|
||||||
this.updateVisibleDecryptionFailures();
|
|
||||||
if (ev.isDecryptionFailure()) return;
|
if (ev.isDecryptionFailure()) return;
|
||||||
this.handleEffects(ev);
|
this.handleEffects(ev);
|
||||||
};
|
};
|
||||||
|
@ -1552,20 +1547,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private updateVisibleDecryptionFailures = throttle(
|
|
||||||
() =>
|
|
||||||
this.setState((prevState) => ({
|
|
||||||
visibleDecryptionFailures:
|
|
||||||
this.messagePanel?.getVisibleDecryptionFailures(
|
|
||||||
// If there were visible failures last time we checked,
|
|
||||||
// add a margin to provide hysteresis and prevent flickering
|
|
||||||
(prevState.visibleDecryptionFailures?.length ?? 0) > 0,
|
|
||||||
) ?? [],
|
|
||||||
})),
|
|
||||||
500,
|
|
||||||
{ leading: false, trailing: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
private onMessageListScroll = (): void => {
|
private onMessageListScroll = (): void => {
|
||||||
if (this.messagePanel?.isAtEndOfLiveTimeline()) {
|
if (this.messagePanel?.isAtEndOfLiveTimeline()) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -1578,7 +1559,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.updateTopUnreadMessagesBar();
|
this.updateTopUnreadMessagesBar();
|
||||||
this.updateVisibleDecryptionFailures();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private resetJumpToEvent = (eventId?: string): void => {
|
private resetJumpToEvent = (eventId?: string): void => {
|
||||||
|
@ -2203,11 +2183,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let decryptionFailureBar: JSX.Element | undefined;
|
|
||||||
if (this.state.visibleDecryptionFailures && this.state.visibleDecryptionFailures.length > 0) {
|
|
||||||
decryptionFailureBar = <DecryptionFailureBar failures={this.state.visibleDecryptionFailures} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.room?.isSpaceRoom() && !this.props.forceTimeline) {
|
if (this.state.room?.isSpaceRoom() && !this.props.forceTimeline) {
|
||||||
return (
|
return (
|
||||||
<SpaceRoomView
|
<SpaceRoomView
|
||||||
|
@ -2233,7 +2208,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
>
|
>
|
||||||
{aux}
|
{aux}
|
||||||
{decryptionFailureBar}
|
|
||||||
</AuxPanel>
|
</AuxPanel>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,270 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2022 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, { useCallback, useContext, useEffect, useState } from "react";
|
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
|
||||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
|
||||||
|
|
||||||
import Modal from "../../../Modal";
|
|
||||||
import { _t } from "../../../languageHandler";
|
|
||||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
|
||||||
import { Action } from "../../../dispatcher/actions";
|
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
|
||||||
import { OpenToTabPayload } from "../../../dispatcher/payloads/OpenToTabPayload";
|
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
|
||||||
import SetupEncryptionDialog from "../dialogs/security/SetupEncryptionDialog";
|
|
||||||
import { SetupEncryptionStore } from "../../../stores/SetupEncryptionStore";
|
|
||||||
import Spinner from "../elements/Spinner";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
failures: MatrixEvent[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Number of milliseconds to display a loading spinner before prompting the user for action
|
|
||||||
const WAIT_PERIOD = 5000;
|
|
||||||
|
|
||||||
export const DecryptionFailureBar: React.FC<IProps> = ({ failures }) => {
|
|
||||||
const context = useContext(MatrixClientContext);
|
|
||||||
|
|
||||||
// Display a spinner for a few seconds before presenting an error message,
|
|
||||||
// in case keys are about to arrive
|
|
||||||
const [waiting, setWaiting] = useState<boolean>(true);
|
|
||||||
useEffect(() => {
|
|
||||||
const timeout = setTimeout(() => setWaiting(false), WAIT_PERIOD);
|
|
||||||
return () => clearTimeout(timeout);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Is this device unverified?
|
|
||||||
const [needsVerification, setNeedsVerification] = useState<boolean>(false);
|
|
||||||
// Does this user have verified devices other than this device?
|
|
||||||
const [hasOtherVerifiedDevices, setHasOtherVerifiedDevices] = useState<boolean>(false);
|
|
||||||
// Does this user have key backups?
|
|
||||||
const [hasKeyBackup, setHasKeyBackup] = useState<boolean>(false);
|
|
||||||
|
|
||||||
// Keep track of session IDs that the user has sent key
|
|
||||||
// requests for using the Resend Key Requests button
|
|
||||||
const [requestedSessions, setRequestedSessions] = useState<Set<string>>(new Set());
|
|
||||||
// Keep track of whether there are any sessions the user has not yet sent requests for
|
|
||||||
const [anyUnrequestedSessions, setAnyUnrequestedSessions] = useState<boolean>(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setAnyUnrequestedSessions(
|
|
||||||
failures.some((event) => {
|
|
||||||
const sessionId = event.getWireContent().session_id;
|
|
||||||
return sessionId && !requestedSessions.has(sessionId);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}, [failures, requestedSessions, setAnyUnrequestedSessions]);
|
|
||||||
|
|
||||||
// Send key requests for any sessions that we haven't previously
|
|
||||||
// sent requests for. This is important if, for instance, we
|
|
||||||
// failed to decrypt a message because a key was withheld (in
|
|
||||||
// which case, we wouldn't bother requesting the key), but have
|
|
||||||
// since verified our device. In that case, now that the device is
|
|
||||||
// verified, other devices might be willing to share the key with us
|
|
||||||
// now.
|
|
||||||
const sendKeyRequests = useCallback(() => {
|
|
||||||
const newRequestedSessions = new Set(requestedSessions);
|
|
||||||
|
|
||||||
for (const event of failures) {
|
|
||||||
const sessionId = event.getWireContent().session_id;
|
|
||||||
if (!sessionId || newRequestedSessions.has(sessionId)) continue;
|
|
||||||
newRequestedSessions.add(sessionId);
|
|
||||||
context.cancelAndResendEventRoomKeyRequest(event);
|
|
||||||
}
|
|
||||||
setRequestedSessions(newRequestedSessions);
|
|
||||||
}, [context, requestedSessions, setRequestedSessions, failures]);
|
|
||||||
|
|
||||||
// Recheck which devices are verified and whether we have key backups
|
|
||||||
const updateDeviceInfo = useCallback(async () => {
|
|
||||||
const deviceId = context.getDeviceId()!;
|
|
||||||
let verified = true; // if we can't get a clear answer, don't bug the user about verifying
|
|
||||||
try {
|
|
||||||
verified = context.checkIfOwnDeviceCrossSigned(deviceId);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error getting device cross-signing info", e);
|
|
||||||
}
|
|
||||||
setNeedsVerification(!verified);
|
|
||||||
|
|
||||||
let otherVerifiedDevices = false;
|
|
||||||
try {
|
|
||||||
const devices = context.getStoredDevicesForUser(context.getUserId()!);
|
|
||||||
otherVerifiedDevices = devices.some(
|
|
||||||
(device) => device.deviceId !== deviceId && context.checkIfOwnDeviceCrossSigned(device.deviceId),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error getting info about other devices", e);
|
|
||||||
}
|
|
||||||
setHasOtherVerifiedDevices(otherVerifiedDevices);
|
|
||||||
|
|
||||||
let keyBackup = false;
|
|
||||||
try {
|
|
||||||
const keys = await context.isSecretStored("m.cross_signing.master");
|
|
||||||
keyBackup = keys !== null && Object.keys(keys).length > 0;
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error getting info about key backups", e);
|
|
||||||
}
|
|
||||||
setHasKeyBackup(keyBackup);
|
|
||||||
}, [context]);
|
|
||||||
|
|
||||||
// Update our device info on initial render, and continue updating
|
|
||||||
// it whenever the client has an update
|
|
||||||
useEffect(() => {
|
|
||||||
updateDeviceInfo().catch(console.error);
|
|
||||||
context.on(CryptoEvent.DevicesUpdated, updateDeviceInfo);
|
|
||||||
return () => {
|
|
||||||
context.off(CryptoEvent.DevicesUpdated, updateDeviceInfo);
|
|
||||||
};
|
|
||||||
}, [context, updateDeviceInfo]);
|
|
||||||
|
|
||||||
const onVerifyClick = (): void => {
|
|
||||||
Modal.createDialog(SetupEncryptionDialog);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeviceListClick = (): void => {
|
|
||||||
const payload: OpenToTabPayload = { action: Action.ViewUserDeviceSettings };
|
|
||||||
defaultDispatcher.dispatch(payload);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onResetClick = (): void => {
|
|
||||||
const store = SetupEncryptionStore.sharedInstance();
|
|
||||||
store.resetConfirm();
|
|
||||||
};
|
|
||||||
|
|
||||||
const statusIndicator = waiting ? (
|
|
||||||
<Spinner w={24} h={24} />
|
|
||||||
) : (
|
|
||||||
<div className="mx_DecryptionFailureBar_start_status_icon" data-testid="decryption-failure-bar-icon" />
|
|
||||||
);
|
|
||||||
|
|
||||||
let className;
|
|
||||||
let headline: JSX.Element;
|
|
||||||
let message: JSX.Element;
|
|
||||||
let button = <React.Fragment />;
|
|
||||||
if (waiting) {
|
|
||||||
className = "mx_DecryptionFailureBar";
|
|
||||||
headline = <React.Fragment>{_t("Decrypting messages…")}</React.Fragment>;
|
|
||||||
message = (
|
|
||||||
<React.Fragment>
|
|
||||||
{_t("Please wait as we try to decrypt your messages. This may take a few moments.")}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
} else if (needsVerification) {
|
|
||||||
if (hasOtherVerifiedDevices || hasKeyBackup) {
|
|
||||||
className = "mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd";
|
|
||||||
headline = <React.Fragment>{_t("Verify this device to access all messages")}</React.Fragment>;
|
|
||||||
message = (
|
|
||||||
<React.Fragment>
|
|
||||||
{_t("This device was unable to decrypt some messages because it has not been verified yet.")}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
button = (
|
|
||||||
<AccessibleButton
|
|
||||||
className="mx_DecryptionFailureBar_end_button"
|
|
||||||
kind="primary"
|
|
||||||
onClick={onVerifyClick}
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
>
|
|
||||||
{_t("Verify")}
|
|
||||||
</AccessibleButton>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
className = "mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd";
|
|
||||||
headline = <React.Fragment>{_t("Reset your keys to prevent future decryption errors")}</React.Fragment>;
|
|
||||||
message = (
|
|
||||||
<React.Fragment>
|
|
||||||
{_t(
|
|
||||||
"You will not be able to access old undecryptable messages, " +
|
|
||||||
"but resetting your keys will allow you to receive new messages.",
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
button = (
|
|
||||||
<AccessibleButton
|
|
||||||
className="mx_DecryptionFailureBar_end_button"
|
|
||||||
kind="primary"
|
|
||||||
onClick={onResetClick}
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
>
|
|
||||||
{_t("Reset")}
|
|
||||||
</AccessibleButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (hasOtherVerifiedDevices) {
|
|
||||||
className = "mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd";
|
|
||||||
headline = <React.Fragment>{_t("Open another device to load encrypted messages")}</React.Fragment>;
|
|
||||||
message = (
|
|
||||||
<React.Fragment>
|
|
||||||
{_t(
|
|
||||||
"This device is requesting decryption keys from your other devices. " +
|
|
||||||
"Opening one of your other devices may speed this up.",
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
button = (
|
|
||||||
<AccessibleButton
|
|
||||||
className="mx_DecryptionFailureBar_end_button"
|
|
||||||
kind="primary_outline"
|
|
||||||
onClick={onDeviceListClick}
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
>
|
|
||||||
{_t("View your device list")}
|
|
||||||
</AccessibleButton>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
className = "mx_DecryptionFailureBar";
|
|
||||||
headline = <React.Fragment>{_t("Some messages could not be decrypted")}</React.Fragment>;
|
|
||||||
message = (
|
|
||||||
<React.Fragment>
|
|
||||||
{_t(
|
|
||||||
"Unfortunately, there are no other verified devices to request decryption keys from. " +
|
|
||||||
"Signing in and verifying other devices may help avoid this situation in the future.",
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let keyRequestButton = <React.Fragment />;
|
|
||||||
if (!needsVerification && hasOtherVerifiedDevices && anyUnrequestedSessions) {
|
|
||||||
className = "mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd";
|
|
||||||
keyRequestButton = (
|
|
||||||
<AccessibleButton
|
|
||||||
className="mx_DecryptionFailureBar_end_button"
|
|
||||||
kind="primary"
|
|
||||||
onClick={sendKeyRequests}
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
>
|
|
||||||
{_t("Resend key requests")}
|
|
||||||
</AccessibleButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<div className="mx_DecryptionFailureBar_start">
|
|
||||||
<div className="mx_DecryptionFailureBar_start_status">
|
|
||||||
<div data-testid="decryption-failure-bar-indicator">{statusIndicator}</div>
|
|
||||||
</div>
|
|
||||||
<div className="mx_DecryptionFailureBar_start_headline">{headline}</div>
|
|
||||||
<div className="mx_DecryptionFailureBar_start_message">{message}</div>
|
|
||||||
</div>
|
|
||||||
<div className="mx_DecryptionFailureBar_end">
|
|
||||||
{button}
|
|
||||||
{keyRequestButton}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1884,18 +1884,6 @@
|
||||||
"Remove %(phone)s?": "Remove %(phone)s?",
|
"Remove %(phone)s?": "Remove %(phone)s?",
|
||||||
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.",
|
"A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.": "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.",
|
||||||
"Phone Number": "Phone Number",
|
"Phone Number": "Phone Number",
|
||||||
"Decrypting messages…": "Decrypting messages…",
|
|
||||||
"Please wait as we try to decrypt your messages. This may take a few moments.": "Please wait as we try to decrypt your messages. This may take a few moments.",
|
|
||||||
"Verify this device to access all messages": "Verify this device to access all messages",
|
|
||||||
"This device was unable to decrypt some messages because it has not been verified yet.": "This device was unable to decrypt some messages because it has not been verified yet.",
|
|
||||||
"Reset your keys to prevent future decryption errors": "Reset your keys to prevent future decryption errors",
|
|
||||||
"You will not be able to access old undecryptable messages, but resetting your keys will allow you to receive new messages.": "You will not be able to access old undecryptable messages, but resetting your keys will allow you to receive new messages.",
|
|
||||||
"Open another device to load encrypted messages": "Open another device to load encrypted messages",
|
|
||||||
"This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.": "This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.",
|
|
||||||
"View your device list": "View your device list",
|
|
||||||
"Some messages could not be decrypted": "Some messages could not be decrypted",
|
|
||||||
"Unfortunately, there are no other verified devices to request decryption keys from. Signing in and verifying other devices may help avoid this situation in the future.": "Unfortunately, there are no other verified devices to request decryption keys from. Signing in and verifying other devices may help avoid this situation in the future.",
|
|
||||||
"Resend key requests": "Resend key requests",
|
|
||||||
"This user has not verified all of their sessions.": "This user has not verified all of their sessions.",
|
"This user has not verified all of their sessions.": "This user has not verified all of their sessions.",
|
||||||
"You have not verified this user.": "You have not verified this user.",
|
"You have not verified this user.": "You have not verified this user.",
|
||||||
"You have verified this user. This user has verified all of their sessions.": "You have verified this user. This user has verified all of their sessions.",
|
"You have verified this user. This user has verified all of their sessions.": "You have verified this user. This user has verified all of their sessions.",
|
||||||
|
|
|
@ -1,449 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2022 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 from "react";
|
|
||||||
import { act, fireEvent, render, screen, waitFor, RenderResult } from "@testing-library/react";
|
|
||||||
import "@testing-library/jest-dom";
|
|
||||||
|
|
||||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
|
||||||
import { DecryptionFailureBar } from "../../../../src/components/views/rooms/DecryptionFailureBar";
|
|
||||||
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
|
||||||
import { Action } from "../../../../src/dispatcher/actions";
|
|
||||||
|
|
||||||
type MockDevice = { deviceId: string };
|
|
||||||
|
|
||||||
const verifiedDevice1: MockDevice = { deviceId: "verified1" };
|
|
||||||
const verifiedDevice2: MockDevice = { deviceId: "verified2" };
|
|
||||||
const unverifiedDevice1: MockDevice = { deviceId: "unverified1" };
|
|
||||||
const unverifiedDevice2: MockDevice = { deviceId: "unverified2" };
|
|
||||||
|
|
||||||
const mockEvent1 = {
|
|
||||||
event: { event_id: "mockEvent1" },
|
|
||||||
getWireContent: () => ({ session_id: "sessionA" }),
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockEvent2 = {
|
|
||||||
event: { event_id: "mockEvent2" },
|
|
||||||
getWireContent: () => ({ session_id: "sessionB" }),
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockEvent3 = {
|
|
||||||
event: { event_id: "mockEvent3" },
|
|
||||||
getWireContent: () => ({ session_id: "sessionB" }),
|
|
||||||
};
|
|
||||||
|
|
||||||
const userId = "@user:example.com";
|
|
||||||
|
|
||||||
let ourDevice: MockDevice | undefined;
|
|
||||||
let allDevices: MockDevice[] | undefined;
|
|
||||||
let keyBackup = false;
|
|
||||||
let callback = async () => {};
|
|
||||||
|
|
||||||
const mockClient = {
|
|
||||||
getUserId: () => userId,
|
|
||||||
getDeviceId: () => ourDevice?.deviceId,
|
|
||||||
getStoredDevicesForUser: () => allDevices,
|
|
||||||
isSecretStored: jest.fn(() => Promise.resolve(keyBackup ? { key: "yes" } : null)),
|
|
||||||
checkIfOwnDeviceCrossSigned: (deviceId: string) => deviceId.startsWith("verified"),
|
|
||||||
downloadKeys: jest.fn(() => {}),
|
|
||||||
cancelAndResendEventRoomKeyRequest: jest.fn(() => {}),
|
|
||||||
on: (_: any, cb: () => Promise<void>) => {
|
|
||||||
callback = cb;
|
|
||||||
},
|
|
||||||
off: () => {},
|
|
||||||
};
|
|
||||||
|
|
||||||
function getBar(wrapper: RenderResult) {
|
|
||||||
return wrapper.container.querySelector(".mx_DecryptionFailureBar");
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("<DecryptionFailureBar />", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.useFakeTimers();
|
|
||||||
jest.spyOn(defaultDispatcher, "dispatch").mockRestore();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
const container = document.body.firstChild;
|
|
||||||
container && document.body.removeChild(container);
|
|
||||||
|
|
||||||
jest.runOnlyPendingTimers();
|
|
||||||
jest.useRealTimers();
|
|
||||||
|
|
||||||
mockClient.cancelAndResendEventRoomKeyRequest.mockClear();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Displays a loading spinner", async () => {
|
|
||||||
ourDevice = unverifiedDevice1;
|
|
||||||
allDevices = [unverifiedDevice1];
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Prompts the user to verify if they have other devices", async () => {
|
|
||||||
ourDevice = unverifiedDevice1;
|
|
||||||
allDevices = [unverifiedDevice1, verifiedDevice1];
|
|
||||||
keyBackup = false;
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Prompts the user to verify if they have backups", async () => {
|
|
||||||
ourDevice = unverifiedDevice1;
|
|
||||||
allDevices = [unverifiedDevice1];
|
|
||||||
keyBackup = true;
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Prompts the user to reset if they have no other verified devices and no backups", async () => {
|
|
||||||
ourDevice = unverifiedDevice1;
|
|
||||||
allDevices = [unverifiedDevice1, unverifiedDevice2];
|
|
||||||
keyBackup = false;
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Recommends opening other devices if there are other verified devices", async () => {
|
|
||||||
ourDevice = verifiedDevice1;
|
|
||||||
allDevices = [verifiedDevice1, verifiedDevice2];
|
|
||||||
keyBackup = false;
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Displays a general error message if there are no other verified devices", async () => {
|
|
||||||
ourDevice = verifiedDevice1;
|
|
||||||
allDevices = [verifiedDevice1, unverifiedDevice1];
|
|
||||||
keyBackup = true;
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Displays button to resend key requests if we are verified", async () => {
|
|
||||||
ourDevice = verifiedDevice1;
|
|
||||||
allDevices = [verifiedDevice1, verifiedDevice2];
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent2,
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent3,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Resend key requests"));
|
|
||||||
|
|
||||||
expect(mockClient.cancelAndResendEventRoomKeyRequest).toHaveBeenCalledTimes(2);
|
|
||||||
expect(mockClient.cancelAndResendEventRoomKeyRequest).toHaveBeenCalledWith(mockEvent1);
|
|
||||||
expect(mockClient.cancelAndResendEventRoomKeyRequest).toHaveBeenCalledWith(mockEvent2);
|
|
||||||
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
it("Displays button to review device list if we are verified", async () => {
|
|
||||||
// stub so we dont have to deal with launching modals
|
|
||||||
jest.spyOn(defaultDispatcher, "dispatch").mockImplementation(() => {});
|
|
||||||
ourDevice = verifiedDevice1;
|
|
||||||
allDevices = [verifiedDevice1, verifiedDevice2];
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent2,
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent3,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("View your device list"));
|
|
||||||
|
|
||||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: Action.ViewUserDeviceSettings });
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Does not display a button to send key requests if we are unverified", async () => {
|
|
||||||
ourDevice = unverifiedDevice1;
|
|
||||||
allDevices = [unverifiedDevice1, verifiedDevice2];
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent2,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Displays the button to resend key requests only if there are sessions we haven't already requested", async () => {
|
|
||||||
ourDevice = verifiedDevice1;
|
|
||||||
allDevices = [verifiedDevice1, verifiedDevice2];
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent3,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Resend key requests"));
|
|
||||||
|
|
||||||
expect(mockClient.cancelAndResendEventRoomKeyRequest).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockClient.cancelAndResendEventRoomKeyRequest).toHaveBeenCalledWith(mockEvent3);
|
|
||||||
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.rerender(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent2,
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent3,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
mockClient.cancelAndResendEventRoomKeyRequest.mockClear();
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Resend key requests"));
|
|
||||||
|
|
||||||
expect(mockClient.cancelAndResendEventRoomKeyRequest).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockClient.cancelAndResendEventRoomKeyRequest).toHaveBeenCalledWith(mockEvent1);
|
|
||||||
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Handles device updates", async () => {
|
|
||||||
ourDevice = unverifiedDevice1;
|
|
||||||
allDevices = [unverifiedDevice1, verifiedDevice2];
|
|
||||||
|
|
||||||
const bar = render(
|
|
||||||
// @ts-ignore
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<DecryptionFailureBar
|
|
||||||
failures={[
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent1,
|
|
||||||
// @ts-ignore
|
|
||||||
mockEvent2,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
,
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await waitFor(() => expect(mockClient.isSecretStored).toHaveBeenCalled());
|
|
||||||
act(() => {
|
|
||||||
jest.advanceTimersByTime(5000);
|
|
||||||
});
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
ourDevice = verifiedDevice1;
|
|
||||||
await act(callback);
|
|
||||||
expect(getBar(bar)).toMatchSnapshot();
|
|
||||||
|
|
||||||
bar.unmount();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,705 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Displays a general error message if there are no other verified devices 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Some messages could not be decrypted
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
Unfortunately, there are no other verified devices to request decryption keys from. Signing in and verifying other devices may help avoid this situation in the future.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Displays a loading spinner 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_Spinner"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
aria-label="Loading…"
|
|
||||||
class="mx_Spinner_icon"
|
|
||||||
data-testid="spinner"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 24px; height: 24px;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Decrypting messages…
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
Please wait as we try to decrypt your messages. This may take a few moments.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Displays button to resend key requests if we are verified 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Open another device to load encrypted messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
View your device list
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Resend key requests
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Displays button to resend key requests if we are verified 2`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Open another device to load encrypted messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
View your device list
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Displays the button to resend key requests only if there are sessions we haven't already requested 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Open another device to load encrypted messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
View your device list
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Resend key requests
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Displays the button to resend key requests only if there are sessions we haven't already requested 2`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Open another device to load encrypted messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
View your device list
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Displays the button to resend key requests only if there are sessions we haven't already requested 3`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Open another device to load encrypted messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
View your device list
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Resend key requests
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Displays the button to resend key requests only if there are sessions we haven't already requested 4`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Open another device to load encrypted messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
View your device list
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Does not display a button to send key requests if we are unverified 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Verify this device to access all messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device was unable to decrypt some messages because it has not been verified yet.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Verify
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Handles device updates 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Verify this device to access all messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device was unable to decrypt some messages because it has not been verified yet.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Verify
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Handles device updates 2`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Open another device to load encrypted messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
View your device list
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Resend key requests
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Prompts the user to reset if they have no other verified devices and no backups 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Reset your keys to prevent future decryption errors
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
You will not be able to access old undecryptable messages, but resetting your keys will allow you to receive new messages.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Prompts the user to verify if they have backups 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Verify this device to access all messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device was unable to decrypt some messages because it has not been verified yet.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Verify
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Prompts the user to verify if they have other devices 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Verify this device to access all messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device was unable to decrypt some messages because it has not been verified yet.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Verify
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`<DecryptionFailureBar /> Recommends opening other devices if there are other verified devices 1`] = `
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar mx_DecryptionFailureBar--withEnd"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid="decryption-failure-bar-indicator"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_status_icon"
|
|
||||||
data-testid="decryption-failure-bar-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_headline"
|
|
||||||
>
|
|
||||||
Open another device to load encrypted messages
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_start_message"
|
|
||||||
>
|
|
||||||
This device is requesting decryption keys from your other devices. Opening one of your other devices may speed this up.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_DecryptionFailureBar_end"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
View your device list
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_AccessibleButton mx_DecryptionFailureBar_end_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-testid="decryption-failure-bar-button"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
Resend key requests
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
Loading…
Reference in a new issue