Merge branch 'develop' into hs/media-previews-server-config
This commit is contained in:
@@ -45,17 +45,16 @@ import { type NonEmptyArray } from "../../../@types/common";
|
||||
import { SDKContext, type SdkContextClass } from "../../../contexts/SDKContext";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
import { ToastContext, useActiveToast } from "../../../contexts/ToastContext";
|
||||
import { EncryptionUserSettingsTab } from "../settings/tabs/user/EncryptionUserSettingsTab";
|
||||
import { EncryptionUserSettingsTab, type State } from "../settings/tabs/user/EncryptionUserSettingsTab";
|
||||
|
||||
interface IProps {
|
||||
initialTabId?: UserTab;
|
||||
showMsc4108QrCode?: boolean;
|
||||
/**
|
||||
* If `true`, the flow for a user to reset their encryption will be shown. In this case, `initialTabId` must be `UserTab.Encryption`.
|
||||
*
|
||||
* If false or undefined, show the tab as normal.
|
||||
/*
|
||||
* The initial state of the Encryption tab.
|
||||
* If undefined, the default state is used ("loading").
|
||||
*/
|
||||
showResetIdentity?: boolean;
|
||||
initialEncryptionState?: State;
|
||||
sdkContext: SdkContextClass;
|
||||
onFinished(): void;
|
||||
}
|
||||
@@ -99,7 +98,7 @@ export default function UserSettingsDialog(props: IProps): JSX.Element {
|
||||
const mjolnirEnabled = useSettingValue("feature_mjolnir");
|
||||
// store these props in state as changing tabs back and forth should clear them
|
||||
const [showMsc4108QrCode, setShowMsc4108QrCode] = useState(props.showMsc4108QrCode);
|
||||
const [showResetIdentity, setShowResetIdentity] = useState(props.showResetIdentity);
|
||||
const [initialEncryptionState, setInitialEncryptionState] = useState(props.initialEncryptionState);
|
||||
|
||||
const getTabs = (): NonEmptyArray<Tab<UserTab>> => {
|
||||
const tabs: Tab<UserTab>[] = [];
|
||||
@@ -195,7 +194,7 @@ export default function UserSettingsDialog(props: IProps): JSX.Element {
|
||||
UserTab.Encryption,
|
||||
_td("settings|encryption|title"),
|
||||
<KeyIcon />,
|
||||
<EncryptionUserSettingsTab initialState={showResetIdentity ? "reset_identity_forgot" : undefined} />,
|
||||
<EncryptionUserSettingsTab initialState={initialEncryptionState} />,
|
||||
"UserSettingsEncryption",
|
||||
),
|
||||
);
|
||||
@@ -234,7 +233,7 @@ export default function UserSettingsDialog(props: IProps): JSX.Element {
|
||||
_setActiveTabId(tabId);
|
||||
// Clear these so switching away from the tab and back to it will not show the QR code again
|
||||
setShowMsc4108QrCode(false);
|
||||
setShowResetIdentity(false);
|
||||
setInitialEncryptionState(undefined);
|
||||
};
|
||||
|
||||
const [activeToast, toastRack] = useActiveToast();
|
||||
|
||||
@@ -29,17 +29,23 @@ interface ResetIdentityPanelProps {
|
||||
onCancelClick: () => void;
|
||||
|
||||
/**
|
||||
* The variant of the panel to show. We show more warnings in the 'compromised' variant (no use in showing a user this
|
||||
* warning if they have to reset because they no longer have their key)
|
||||
*
|
||||
* "compromised" is shown when the user chooses 'reset' explicitly in settings, usually because they believe their
|
||||
* identity has been compromised.
|
||||
*
|
||||
* "forgot" is shown when the user has just forgotten their passphrase.
|
||||
* The variant of the panel to show. We show more warnings in the 'compromised' variant (no use in showing a user
|
||||
* this warning if they have to reset because they no longer have their key)
|
||||
*/
|
||||
variant: "compromised" | "forgot";
|
||||
variant: ResetIdentityPanelVariant;
|
||||
}
|
||||
|
||||
/**
|
||||
* "compromised" is shown when the user chooses 'reset' explicitly in settings, usually because they believe their
|
||||
* identity has been compromised.
|
||||
*
|
||||
* "sync_failed" is shown when the user tried to recover their identity but the process failed, probably because
|
||||
* the required information is missing from recovery.
|
||||
*
|
||||
* "forgot" is shown when the user has just forgotten their passphrase.
|
||||
*/
|
||||
export type ResetIdentityPanelVariant = "compromised" | "forgot" | "sync_failed";
|
||||
|
||||
/**
|
||||
* The panel for resetting the identity of the current user.
|
||||
*/
|
||||
@@ -58,15 +64,7 @@ export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetId
|
||||
pages={[_t("settings|encryption|title"), _t("settings|encryption|advanced|breadcrumb_page")]}
|
||||
onPageClick={onCancelClick}
|
||||
/>
|
||||
<EncryptionCard
|
||||
Icon={ErrorIcon}
|
||||
destructive={true}
|
||||
title={
|
||||
variant === "forgot"
|
||||
? _t("settings|encryption|advanced|breadcrumb_title_forgot")
|
||||
: _t("settings|encryption|advanced|breadcrumb_title")
|
||||
}
|
||||
>
|
||||
<EncryptionCard Icon={ErrorIcon} destructive={true} title={titleForVariant(variant)}>
|
||||
<EncryptionCardEmphasisedContent>
|
||||
<VisualList>
|
||||
<VisualListItem Icon={CheckIcon} success={true}>
|
||||
@@ -117,3 +115,16 @@ export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetId
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function titleForVariant(variant: ResetIdentityPanelVariant): string {
|
||||
switch (variant) {
|
||||
case "compromised":
|
||||
return _t("settings|encryption|advanced|breadcrumb_title");
|
||||
case "sync_failed":
|
||||
return _t("settings|encryption|advanced|breadcrumb_title_sync_failed");
|
||||
|
||||
default:
|
||||
case "forgot":
|
||||
return _t("settings|encryption|advanced|breadcrumb_title_forgot");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import SetupEncryptionDialog from "../../../dialogs/security/SetupEncryptionDial
|
||||
import { SettingsSection } from "../../shared/SettingsSection";
|
||||
import { SettingsSubheader } from "../../SettingsSubheader";
|
||||
import { AdvancedPanel } from "../../encryption/AdvancedPanel";
|
||||
import { ResetIdentityPanel } from "../../encryption/ResetIdentityPanel";
|
||||
import { ResetIdentityPanel, type ResetIdentityPanelVariant } from "../../encryption/ResetIdentityPanel";
|
||||
import { RecoveryPanelOutOfSync } from "../../encryption/RecoveryPanelOutOfSync";
|
||||
import { useTypedEventEmitter } from "../../../../../hooks/useEventEmitter";
|
||||
import { KeyStoragePanel } from "../../encryption/KeyStoragePanel";
|
||||
@@ -39,6 +39,7 @@ import { DeleteKeyStoragePanel } from "../../encryption/DeleteKeyStoragePanel";
|
||||
* This happens when the user doesn't have a key a recovery key and the user clicks on "Set up recovery key" button of the RecoveryPanel.
|
||||
* - "reset_identity_compromised": The panel to show when the user is resetting their identity, in the case where their key is compromised.
|
||||
* - "reset_identity_forgot": The panel to show when the user is resetting their identity, in the case where they forgot their recovery key.
|
||||
* - "reset_identity_sync_failed": The panel to show when the user us resetting their identity, in the case where recovery failed.
|
||||
* - "secrets_not_cached": The secrets are not cached locally. This can happen if we verified another device and secret-gossiping failed, or the other device itself lacked the secrets.
|
||||
* If the "set_up_encryption" and "secrets_not_cached" conditions are both filled, "set_up_encryption" prevails.
|
||||
* - "key_storage_delete": The confirmation page asking if the user really wants to turn off key storage.
|
||||
@@ -52,6 +53,7 @@ export type State =
|
||||
| "set_recovery_key"
|
||||
| "reset_identity_compromised"
|
||||
| "reset_identity_forgot"
|
||||
| "reset_identity_sync_failed"
|
||||
| "secrets_not_cached"
|
||||
| "key_storage_delete";
|
||||
|
||||
@@ -120,9 +122,10 @@ export function EncryptionUserSettingsTab({ initialState = "loading" }: Props):
|
||||
break;
|
||||
case "reset_identity_compromised":
|
||||
case "reset_identity_forgot":
|
||||
case "reset_identity_sync_failed":
|
||||
content = (
|
||||
<ResetIdentityPanel
|
||||
variant={state === "reset_identity_compromised" ? "compromised" : "forgot"}
|
||||
variant={findResetVariant(state)}
|
||||
onCancelClick={checkEncryptionState}
|
||||
onFinish={checkEncryptionState}
|
||||
/>
|
||||
@@ -140,6 +143,23 @@ export function EncryptionUserSettingsTab({ initialState = "loading" }: Props):
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given what state we want the tab to be in, what variant of the
|
||||
* ResetIdentityPanel do we need?
|
||||
*/
|
||||
function findResetVariant(state: State): ResetIdentityPanelVariant {
|
||||
switch (state) {
|
||||
case "reset_identity_compromised":
|
||||
return "compromised";
|
||||
case "reset_identity_sync_failed":
|
||||
return "sync_failed";
|
||||
|
||||
default:
|
||||
case "reset_identity_forgot":
|
||||
return "forgot";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to check if the user needs:
|
||||
* - to go through the SetupEncryption flow.
|
||||
|
||||
@@ -2529,6 +2529,7 @@
|
||||
"breadcrumb_third_description": "You will need to verify all your existing devices and contacts again",
|
||||
"breadcrumb_title": "Are you sure you want to reset your identity?",
|
||||
"breadcrumb_title_forgot": "Forgot your recovery key? You’ll need to reset your identity.",
|
||||
"breadcrumb_title_sync_failed": "Failed to sync key storage. You need to reset your identity.",
|
||||
"breadcrumb_warning": "Only do this if you believe your account has been compromised.",
|
||||
"details_title": "Encryption details",
|
||||
"do_not_close_warning": "Do not close this window until the reset is finished",
|
||||
|
||||
@@ -14,7 +14,7 @@ import Modal from "../Modal";
|
||||
import { _t } from "../languageHandler";
|
||||
import DeviceListener from "../DeviceListener";
|
||||
import SetupEncryptionDialog from "../components/views/dialogs/security/SetupEncryptionDialog";
|
||||
import { accessSecretStorage } from "../SecurityManager";
|
||||
import { AccessCancelledError, accessSecretStorage } from "../SecurityManager";
|
||||
import ToastStore from "../stores/ToastStore";
|
||||
import GenericToast from "../components/views/toasts/GenericToast";
|
||||
import { ModuleRunner } from "../modules/ModuleRunner";
|
||||
@@ -153,6 +153,8 @@ export const showToast = (kind: Kind): void => {
|
||||
);
|
||||
try {
|
||||
await accessSecretStorage();
|
||||
} catch (error) {
|
||||
onAccessSecretStorageFailed(error as Error);
|
||||
} finally {
|
||||
modal.close();
|
||||
}
|
||||
@@ -165,7 +167,7 @@ export const showToast = (kind: Kind): void => {
|
||||
const payload: OpenToTabPayload = {
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Encryption,
|
||||
props: { showResetIdentity: true },
|
||||
props: { initialEncryptionState: "reset_identity_forgot" },
|
||||
};
|
||||
defaultDispatcher.dispatch(payload);
|
||||
} else {
|
||||
@@ -173,6 +175,27 @@ export const showToast = (kind: Kind): void => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* We tried to accessSecretStorage, which triggered us to ask for the
|
||||
* recovery key, but this failed. If the user just gave up, that is fine,
|
||||
* but if not, that means downloading encryption info from 4S did not fix
|
||||
* the problem we identified. Presumably, something is wrong with what
|
||||
* they have in 4S: we tell them to reset their identity.
|
||||
*/
|
||||
const onAccessSecretStorageFailed = (error: Error): void => {
|
||||
if (error instanceof AccessCancelledError) {
|
||||
// The user cancelled the dialog - just allow it to close
|
||||
} else {
|
||||
// A real error happened - jump to the reset identity tab
|
||||
const payload: OpenToTabPayload = {
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Encryption,
|
||||
props: { initialEncryptionState: "reset_identity_sync_failed" },
|
||||
};
|
||||
defaultDispatcher.dispatch(payload);
|
||||
}
|
||||
};
|
||||
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
key: TOAST_KEY,
|
||||
title: getTitle(kind),
|
||||
|
||||
@@ -54,4 +54,13 @@ describe("<ResetIdentityPanel />", () => {
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display the 'sync failed' variant correctly", async () => {
|
||||
const onFinish = jest.fn();
|
||||
const { asFragment } = render(
|
||||
<ResetIdentityPanel variant="sync_failed" onFinish={onFinish} onCancelClick={jest.fn()} />,
|
||||
withClientContextRenderOptions(matrixClient),
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -182,6 +182,188 @@ exports[`<ResetIdentityPanel /> should display the 'forgot recovery key' variant
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<ResetIdentityPanel /> should display the 'sync failed' variant correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<nav
|
||||
class="_breadcrumb_1xygz_8"
|
||||
>
|
||||
<button
|
||||
aria-label="Back"
|
||||
class="_icon-button_m2erp_8 _subtle-bg_m2erp_29"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 28px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_zr2a0_17"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<ol
|
||||
class="_pages_1xygz_17"
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
class="_link_1v5rz_8"
|
||||
data-kind="primary"
|
||||
data-size="small"
|
||||
rel="noreferrer noopener"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Encryption
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<span
|
||||
aria-current="page"
|
||||
class="_last-page_1xygz_30"
|
||||
>
|
||||
Reset encryption
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<div
|
||||
class="mx_EncryptionCard"
|
||||
>
|
||||
<div
|
||||
class="mx_EncryptionCard_header"
|
||||
>
|
||||
<div
|
||||
class="_content_o77nw_8 _destructive_o77nw_34"
|
||||
data-size="large"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h2
|
||||
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
|
||||
>
|
||||
Failed to sync key storage. You need to reset your identity.
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Flex mx_EncryptionCard_emphasisedContent"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<ul
|
||||
class="_visual-list_15wzx_8"
|
||||
>
|
||||
<li
|
||||
class="_visual-list-item_1ma3e_8"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_visual-list-item-icon_1ma3e_17 _visual-list-item-icon-success_1ma3e_22"
|
||||
fill="currentColor"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
width="24px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||
/>
|
||||
</svg>
|
||||
Your account details, contacts, preferences, and chat list will be kept
|
||||
</li>
|
||||
<li
|
||||
class="_visual-list-item_1ma3e_8"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_visual-list-item-icon_1ma3e_17"
|
||||
fill="currentColor"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
width="24px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
|
||||
/>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
You will lose any message history that’s stored only on the server
|
||||
</li>
|
||||
<li
|
||||
class="_visual-list-item_1ma3e_8"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="_visual-list-item-icon_1ma3e_17"
|
||||
fill="currentColor"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
width="24px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
|
||||
/>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
You will need to verify all your existing devices and contacts again
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
class="mx_EncryptionCard_buttons"
|
||||
>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
class="_button_vczzf_8 _destructive_vczzf_107"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
<button
|
||||
class="_button_vczzf_8"
|
||||
data-kind="tertiary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<ResetIdentityPanel /> should reset the encryption when the continue button is clicked 1`] = `
|
||||
<DocumentFragment>
|
||||
<nav
|
||||
|
||||
@@ -151,18 +151,30 @@ describe("<EncryptionUserSettingsTab />", () => {
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should enter reset flow when showResetIdentity is set", async () => {
|
||||
it("should enter 'Forgot recovery' flow when initialState is set to 'reset_identity_forgot'", async () => {
|
||||
jest.spyOn(matrixClient.getCrypto()!, "getActiveSessionBackupVersion").mockResolvedValue("1");
|
||||
|
||||
renderComponent({ initialState: "reset_identity_forgot" });
|
||||
|
||||
await expect(
|
||||
expect(
|
||||
await screen.findByRole("heading", {
|
||||
name: "Forgot your recovery key? You’ll need to reset your identity.",
|
||||
}),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
it("should do 'Failed to sync' reset flow when initialState is set to 'reset_identity_sync_failed'", async () => {
|
||||
jest.spyOn(matrixClient.getCrypto()!, "getActiveSessionBackupVersion").mockResolvedValue("1");
|
||||
|
||||
renderComponent({ initialState: "reset_identity_sync_failed" });
|
||||
|
||||
expect(
|
||||
await screen.findByRole("heading", {
|
||||
name: "Failed to sync key storage. You need to reset your identity.",
|
||||
}),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
it("should update when key backup status event is fired", async () => {
|
||||
jest.spyOn(matrixClient.getCrypto()!, "getActiveSessionBackupVersion").mockResolvedValue("1");
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import React from "react";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import * as SecurityManager from "../../../src/SecurityManager";
|
||||
import ToastContainer from "../../../src/components/structures/ToastContainer";
|
||||
import { Kind, showToast } from "../../../src/toasts/SetupEncryptionToast";
|
||||
import dis from "../../../src/dispatcher/dispatcher";
|
||||
@@ -57,7 +58,24 @@ describe("SetupEncryptionToast", () => {
|
||||
expect(dis.dispatch).toHaveBeenCalledWith({
|
||||
action: "view_user_settings",
|
||||
initialTabId: "USER_ENCRYPTION_TAB",
|
||||
props: { showResetIdentity: true },
|
||||
props: { initialEncryptionState: "reset_identity_forgot" },
|
||||
});
|
||||
});
|
||||
|
||||
it("should open settings to the reset flow when recovering fails clicked", async () => {
|
||||
jest.spyOn(SecurityManager, "accessSecretStorage").mockImplementation(async () => {
|
||||
throw new Error("Something went wrong while recovering!");
|
||||
});
|
||||
|
||||
showToast(Kind.KEY_STORAGE_OUT_OF_SYNC);
|
||||
|
||||
const user = userEvent.setup();
|
||||
await user.click(await screen.findByText("Enter recovery key"));
|
||||
|
||||
expect(dis.dispatch).toHaveBeenCalledWith({
|
||||
action: "view_user_settings",
|
||||
initialTabId: "USER_ENCRYPTION_TAB",
|
||||
props: { initialEncryptionState: "reset_identity_forgot" },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user