Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wire up the "Forgot recovery key" button for the "Key storage out of sync" toast #29138

Merged
merged 16 commits into from
Feb 4, 2025
Merged
52 changes: 52 additions & 0 deletions playwright/e2e/crypto/toasts.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/

import { GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api";

import { test, expect } from "../../element-web-test";
import { createBot, deleteCachedSecrets, logIntoElement } from "./utils";

test.describe("Key storage out of sync toast", () => {
let recoveryKey: GeneratedSecretStorageKey;

test.beforeEach(async ({ page, homeserver, credentials }) => {
const res = await createBot(page, homeserver, credentials);
recoveryKey = res.recoveryKey;

await logIntoElement(page, credentials, recoveryKey.encodedPrivateKey);

await deleteCachedSecrets(page);

// We won't be prompted for crypto setup unless we have an e2e room, so make one
await page.getByRole("button", { name: "Add room" }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
await page.getByRole("textbox", { name: "Name" }).fill("Test room");
await page.getByRole("button", { name: "Create room" }).click();
});

test("should prompt for recovery key if 'enter recovery key' pressed", { tag: "@screenshot" }, async ({ page }) => {
await expect(page.getByRole("alert").first()).toMatchScreenshot("key-storage-out-of-sync-toast.png");

Check failure on line 32 in playwright/e2e/crypto/toasts.spec.ts

View workflow job for this annotation

GitHub Actions / Run Tests [Chrome] 2/6

[Chrome] › crypto/toasts.spec.ts:31:9 › Key storage out of sync toast › should prompt for recovery key if 'enter recovery key' pressed @screenshot

1) [Chrome] › crypto/toasts.spec.ts:31:9 › Key storage out of sync toast › should prompt for recovery key if 'enter recovery key' pressed @screenshot Error: Timed out 5000ms waiting for expect(locator).toHaveScreenshot(expected) Timeout 5000ms exceeded. Expected: /home/runner/work/element-web/element-web/playwright/snapshots/crypto/toasts.spec.ts/key-storage-out-of-sync-toast-linux.png Call log: - expect.toHaveScreenshot(key-storage-out-of-sync-toast.png) with timeout 5000ms - verifying given screenshot expectation - waiting for getByRole('alert').first() - locator resolved to <div role="alert" class="mx_NonUrgentToastContainer"></div> - taking element screenshot - disabled all CSS animations - waiting for fonts to load... - fonts loaded - attempting scroll into view action 2 × waiting for element to be stable - element is not visible - retrying scroll into view action - waiting 20ms 2 × waiting for element to be stable - element is not visible - retrying scroll into view action - waiting 100ms 9 × waiting for element to be stable - element is not visible - retrying scroll into view action - waiting 500ms - Timeout 5000ms exceeded. 30 | 31 | test("should prompt for recovery key if 'enter recovery key' pressed", { tag: "@screenshot" }, async ({ page }) => { > 32 | await expect(page.getByRole("alert").first()).toMatchScreenshot("key-storage-out-of-sync-toast.png"); | ^ 33 | 34 | await page.getByRole("button", { name: "Enter recovery key" }).click(); 35 | await page.locator(".mx_Dialog").getByRole("button", { name: "use your Security Key" }).click(); at /home/runner/work/element-web/element-web/playwright/e2e/crypto/toasts.spec.ts:32:55

Check failure on line 32 in playwright/e2e/crypto/toasts.spec.ts

View workflow job for this annotation

GitHub Actions / Run Tests [Chrome] 2/6

[Chrome] › crypto/toasts.spec.ts:31:9 › Key storage out of sync toast › should prompt for recovery key if 'enter recovery key' pressed @screenshot

1) [Chrome] › crypto/toasts.spec.ts:31:9 › Key storage out of sync toast › should prompt for recovery key if 'enter recovery key' pressed @screenshot Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toHaveScreenshot(expected) Timeout 5000ms exceeded. Expected: /home/runner/work/element-web/element-web/playwright/snapshots/crypto/toasts.spec.ts/key-storage-out-of-sync-toast-linux.png Call log: - expect.toHaveScreenshot(key-storage-out-of-sync-toast.png) with timeout 5000ms - verifying given screenshot expectation - waiting for getByRole('alert').first() - locator resolved to <div role="alert" class="mx_NonUrgentToastContainer"></div> - taking element screenshot - disabled all CSS animations - waiting for fonts to load... - fonts loaded - attempting scroll into view action 2 × waiting for element to be stable - element is not visible - retrying scroll into view action - waiting 20ms 2 × waiting for element to be stable - element is not visible - retrying scroll into view action - waiting 100ms 9 × waiting for element to be stable - element is not visible - retrying scroll into view action - waiting 500ms - Timeout 5000ms exceeded. 30 | 31 | test("should prompt for recovery key if 'enter recovery key' pressed", { tag: "@screenshot" }, async ({ page }) => { > 32 | await expect(page.getByRole("alert").first()).toMatchScreenshot("key-storage-out-of-sync-toast.png"); | ^ 33 | 34 | await page.getByRole("button", { name: "Enter recovery key" }).click(); 35 | await page.locator(".mx_Dialog").getByRole("button", { name: "use your Security Key" }).click(); at /home/runner/work/element-web/element-web/playwright/e2e/crypto/toasts.spec.ts:32:55

Check failure on line 32 in playwright/e2e/crypto/toasts.spec.ts

View workflow job for this annotation

GitHub Actions / Run Tests [Chrome] 2/6

[Chrome] › crypto/toasts.spec.ts:31:9 › Key storage out of sync toast › should prompt for recovery key if 'enter recovery key' pressed @screenshot

1) [Chrome] › crypto/toasts.spec.ts:31:9 › Key storage out of sync toast › should prompt for recovery key if 'enter recovery key' pressed @screenshot Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 5000ms waiting for expect(locator).toHaveScreenshot(expected) Timeout 5000ms exceeded. Expected: /home/runner/work/element-web/element-web/playwright/snapshots/crypto/toasts.spec.ts/key-storage-out-of-sync-toast-linux.png Call log: - expect.toHaveScreenshot(key-storage-out-of-sync-toast.png) with timeout 5000ms - verifying given screenshot expectation - waiting for getByRole('alert').first() - locator resolved to <div role="alert" class="mx_NonUrgentToastContainer"></div> - taking element screenshot - disabled all CSS animations - waiting for fonts to load... - fonts loaded - attempting scroll into view action 2 × waiting for element to be stable - element is not visible - retrying scroll into view action - waiting 20ms 2 × waiting for element to be stable - element is not visible - retrying scroll into view action - waiting 100ms 9 × waiting for element to be stable - element is not visible - retrying scroll into view action - waiting 500ms - Timeout 5000ms exceeded. 30 | 31 | test("should prompt for recovery key if 'enter recovery key' pressed", { tag: "@screenshot" }, async ({ page }) => { > 32 | await expect(page.getByRole("alert").first()).toMatchScreenshot("key-storage-out-of-sync-toast.png"); | ^ 33 | 34 | await page.getByRole("button", { name: "Enter recovery key" }).click(); 35 | await page.locator(".mx_Dialog").getByRole("button", { name: "use your Security Key" }).click(); at /home/runner/work/element-web/element-web/playwright/e2e/crypto/toasts.spec.ts:32:55

await page.getByRole("button", { name: "Enter recovery key" }).click();
await page.locator(".mx_Dialog").getByRole("button", { name: "use your Security Key" }).click();

await page.getByRole("textbox", { name: "Security key" }).fill(recoveryKey.encodedPrivateKey);
await page.getByRole("button", { name: "Continue" }).click();

await expect(page.getByRole("button", { name: "Enter recovery key" })).not.toBeVisible();
});

test("should open settings to reset flow if 'forgot recovery key' pressed", async ({ page, app, credentials }) => {
await expect(page.getByRole("button", { name: "Enter recovery key" })).toBeVisible();

await page.getByRole("button", { name: "Forgot recovery key?" }).click();

await expect(
page.getByRole("heading", { name: "Forgot your recovery key? You’ll need to reset your identity." }),
).toBeVisible();
});
});
1 change: 1 addition & 0 deletions playwright/e2e/crypto/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@
// if a securityKey was given, verify the new device
if (securityKey !== undefined) {
await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key" }).click();
await page.locator(".mx_Dialog").getByRole("button", { name: "use your Security Key" }).click();

Check failure on line 217 in playwright/e2e/crypto/utils.ts

View workflow job for this annotation

GitHub Actions / Run Tests [Chrome] 1/6

[Chrome] › crypto/event-shields.spec.ts:179:13 › Cryptography › event shields › Should show a grey padlock for a key restored from backup

1) [Chrome] › crypto/event-shields.spec.ts:179:13 › Cryptography › event shields › Should show a grey padlock for a key restored from backup Error: locator.click: Test timeout of 90000ms exceeded. Call log: - waiting for locator('.mx_Dialog').getByRole('button', { name: 'use your Security Key' }) at crypto/utils.ts:217 215 | if (securityKey !== undefined) { 216 | await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key" }).click(); > 217 | await page.locator(".mx_Dialog").getByRole("button", { name: "use your Security Key" }).click(); | ^ 218 | // Fill in the security key 219 | await page.locator(".mx_Dialog").locator('input[type="password"]').fill(securityKey); 220 | await page.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click(); at logIntoElement (/home/runner/work/element-web/element-web/playwright/e2e/crypto/utils.ts:217:97) at /home/runner/work/element-web/element-web/playwright/e2e/crypto/event-shields.spec.ts:213:13

Check failure on line 217 in playwright/e2e/crypto/utils.ts

View workflow job for this annotation

GitHub Actions / Run Tests [Chrome] 1/6

[Chrome] › crypto/event-shields.spec.ts:179:13 › Cryptography › event shields › Should show a grey padlock for a key restored from backup

1) [Chrome] › crypto/event-shields.spec.ts:179:13 › Cryptography › event shields › Should show a grey padlock for a key restored from backup Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: locator.click: Test timeout of 90000ms exceeded. Call log: - waiting for locator('.mx_Dialog').getByRole('button', { name: 'use your Security Key' }) at crypto/utils.ts:217 215 | if (securityKey !== undefined) { 216 | await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key" }).click(); > 217 | await page.locator(".mx_Dialog").getByRole("button", { name: "use your Security Key" }).click(); | ^ 218 | // Fill in the security key 219 | await page.locator(".mx_Dialog").locator('input[type="password"]').fill(securityKey); 220 | await page.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click(); at logIntoElement (/home/runner/work/element-web/element-web/playwright/e2e/crypto/utils.ts:217:97) at /home/runner/work/element-web/element-web/playwright/e2e/crypto/event-shields.spec.ts:213:13

Check failure on line 217 in playwright/e2e/crypto/utils.ts

View workflow job for this annotation

GitHub Actions / Run Tests [Chrome] 1/6

[Chrome] › crypto/event-shields.spec.ts:179:13 › Cryptography › event shields › Should show a grey padlock for a key restored from backup

1) [Chrome] › crypto/event-shields.spec.ts:179:13 › Cryptography › event shields › Should show a grey padlock for a key restored from backup Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: locator.click: Test timeout of 90000ms exceeded. Call log: - waiting for locator('.mx_Dialog').getByRole('button', { name: 'use your Security Key' }) at crypto/utils.ts:217 215 | if (securityKey !== undefined) { 216 | await page.locator(".mx_AuthPage").getByRole("button", { name: "Verify with Security Key" }).click(); > 217 | await page.locator(".mx_Dialog").getByRole("button", { name: "use your Security Key" }).click(); | ^ 218 | // Fill in the security key 219 | await page.locator(".mx_Dialog").locator('input[type="password"]').fill(securityKey); 220 | await page.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click(); at logIntoElement (/home/runner/work/element-web/element-web/playwright/e2e/crypto/utils.ts:217:97) at /home/runner/work/element-web/element-web/playwright/e2e/crypto/event-shields.spec.ts:213:13
// Fill in the security key
await page.locator(".mx_Dialog").locator('input[type="password"]').fill(securityKey);
await page.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 11 additions & 3 deletions src/components/views/dialogs/UserSettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { EncryptionUserSettingsTab } from "../settings/tabs/user/EncryptionUserS
interface IProps {
initialTabId?: UserTab;
showMsc4108QrCode?: boolean;
showResetIdentity?: boolean;
sdkContext: SdkContextClass;
onFinished(): void;
}
Expand Down Expand Up @@ -91,8 +92,9 @@ function titleForTabID(tabId: UserTab): React.ReactNode {
export default function UserSettingsDialog(props: IProps): JSX.Element {
const voipEnabled = useSettingValue(UIFeature.Voip);
const mjolnirEnabled = useSettingValue("feature_mjolnir");
// store this prop in state as changing tabs back and forth should clear it
// store these props in state as changing tabs back and forth should clear it
const [showMsc4108QrCode, setShowMsc4108QrCode] = useState(props.showMsc4108QrCode);
const [showResetIdentity, setShowResetIdentity] = useState(props.showResetIdentity);

const getTabs = (): NonEmptyArray<Tab<UserTab>> => {
const tabs: Tab<UserTab>[] = [];
Expand Down Expand Up @@ -184,7 +186,12 @@ export default function UserSettingsDialog(props: IProps): JSX.Element {
);

tabs.push(
new Tab(UserTab.Encryption, _td("settings|encryption|title"), <KeyIcon />, <EncryptionUserSettingsTab />),
new Tab(
UserTab.Encryption,
_td("settings|encryption|title"),
<KeyIcon />,
<EncryptionUserSettingsTab initialState={showResetIdentity ? "reset_identity_forgot" : undefined} />,
),
);

if (showLabsFlags() || SettingsStore.getFeatureSettingNames().some((k) => SettingsStore.getBetaInfo(k))) {
Expand Down Expand Up @@ -219,8 +226,9 @@ export default function UserSettingsDialog(props: IProps): JSX.Element {
const [activeTabId, _setActiveTabId] = useActiveTabWithDefault(getTabs(), UserTab.Account, props.initialTabId);
const setActiveTabId = (tabId: UserTab): void => {
_setActiveTabId(tabId);
// Clear this so switching away from the tab and back to it will not show the QR code again
// Clear these so switching away from the tab and back to it will not show the QR code again
setShowMsc4108QrCode(false);
setShowResetIdentity(false);
};

const [activeToast, toastRack] = useActiveToast();
Expand Down
19 changes: 16 additions & 3 deletions src/components/views/settings/encryption/ResetIdentityPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,21 @@ interface ResetIdentityPanelProps {
* Called when the cancel button is clicked or when we go back in the breadcrumbs.
*/
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.
*/
variant: "compromised" | "forgot";
}

/**
* The panel for resetting the identity of the current user.
*/
export function ResetIdentityPanel({ onCancelClick, onFinish }: ResetIdentityPanelProps): JSX.Element {
export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetIdentityPanelProps): JSX.Element {
const matrixClient = useMatrixClientContext();

return (
Expand All @@ -44,7 +53,11 @@ export function ResetIdentityPanel({ onCancelClick, onFinish }: ResetIdentityPan
<EncryptionCard
Icon={ErrorIcon}
destructive={true}
title={_t("settings|encryption|advanced|breadcrumb_title")}
title={
variant === "forgot"
? _t("settings|encryption|advanced|breadcrumb_title_forgot")
: _t("settings|encryption|advanced|breadcrumb_title")
}
className="mx_ResetIdentityPanel"
>
<div className="mx_ResetIdentityPanel_content">
Expand All @@ -59,7 +72,7 @@ export function ResetIdentityPanel({ onCancelClick, onFinish }: ResetIdentityPan
{_t("settings|encryption|advanced|breadcrumb_third_description")}
</VisualListItem>
</VisualList>
<span>{_t("settings|encryption|advanced|breadcrumb_warning")}</span>
{variant === "compromised" && <span>{_t("settings|encryption|advanced|breadcrumb_warning")}</span>}
</div>
<div className="mx_ResetIdentityPanel_footer">
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,35 @@ import { RecoveryPanelOutOfSync } from "../../encryption/RecoveryPanelOutOfSync"
* This happens when the user has a recovery key and the user clicks on "Change recovery key" button of the RecoveryPanel.
* - "set_recovery_key": The panel to show when the user is setting up their recovery key.
* 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": The panel to show when the user is resetting their identity.
* - `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.
* - "reset_identity_compromised": The panel to show when the user is resetting their identity, in te 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.
* - `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.
*
*/
type State =
export type State =
| "loading"
| "main"
| "set_up_encryption"
| "change_recovery_key"
| "set_recovery_key"
| "reset_identity"
| "reset_identity_compromised"
| "reset_identity_forgot"
| "secrets_not_cached";

export function EncryptionUserSettingsTab(): JSX.Element {
const [state, setState] = useState<State>("loading");
const checkEncryptionState = useCheckEncryptionState(setState);
interface EncryptionUserSettingsTabProps {
/**
* If the tab should start in a state other than the deasult
*/
initialState?: State;
}

/**
* The encryption settings tab.
*/
export function EncryptionUserSettingsTab({ initialState = "loading" }: EncryptionUserSettingsTabProps): JSX.Element {
const [state, setState] = useState<State>(initialState);

const checkEncryptionState = useCheckEncryptionState(state, setState);

let content: JSX.Element;
switch (state) {
Expand All @@ -70,7 +82,7 @@ export function EncryptionUserSettingsTab(): JSX.Element {
}
/>
<Separator kind="section" />
<AdvancedPanel onResetIdentityClick={() => setState("reset_identity")} />
<AdvancedPanel onResetIdentityClick={() => setState("reset_identity_compromised")} />
</>
);
break;
Expand All @@ -84,8 +96,23 @@ export function EncryptionUserSettingsTab(): JSX.Element {
/>
);
break;
case "reset_identity":
content = <ResetIdentityPanel onCancelClick={() => setState("main")} onFinish={() => setState("main")} />;
case "reset_identity_compromised":
content = (
<ResetIdentityPanel
variant="compromised"
onCancelClick={() => setState("main")}
onFinish={() => setState("main")}
/>
);
break;
case "reset_identity_forgot":
content = (
<ResetIdentityPanel
variant="forgot"
onCancelClick={() => setState("main")}
onFinish={() => setState("main")}
/>
);
break;
}

Expand All @@ -111,7 +138,7 @@ export function EncryptionUserSettingsTab(): JSX.Element {
* @param setState - callback passed from the EncryptionUserSettingsTab to set the current `State`.
* @returns a callback function, which will re-run the logic and update the state.
*/
function useCheckEncryptionState(setState: (state: State) => void): () => Promise<void> {
function useCheckEncryptionState(state: State, setState: (state: State) => void): () => Promise<void> {
const matrixClient = useMatrixClientContext();

const checkEncryptionState = useCallback(async () => {
Expand All @@ -129,8 +156,8 @@ function useCheckEncryptionState(setState: (state: State) => void): () => Promis

// Initialise the state when the component is mounted
useEffect(() => {
checkEncryptionState();
}, [checkEncryptionState]);
if (state === "loading") checkEncryptionState();
}, [checkEncryptionState, state]);

// Also return the callback so that the component can re-run the logic.
return checkEncryptionState;
Expand Down
1 change: 1 addition & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2469,6 +2469,7 @@
"breadcrumb_second_description": "You will lose any message history that’s stored only on the server",
"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_warning": "Only do this if you believe your account has been compromised.",
"details_title": "Encryption details",
"export_keys": "Export keys",
Expand Down
27 changes: 20 additions & 7 deletions src/toasts/SetupEncryptionToast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import GenericToast from "../components/views/toasts/GenericToast";
import { ModuleRunner } from "../modules/ModuleRunner";
import { SetupEncryptionStore } from "../stores/SetupEncryptionStore";
import Spinner from "../components/views/elements/Spinner";
import { OpenToTabPayload } from "../dispatcher/payloads/OpenToTabPayload";
import { Action } from "../dispatcher/actions";
import { UserTab } from "../components/views/dialogs/UserTab";
import defaultDispatcher from "../dispatcher/dispatcher";

const TOAST_KEY = "setupencryption";

Expand Down Expand Up @@ -104,10 +108,6 @@ export enum Kind {
KEY_STORAGE_OUT_OF_SYNC = "key_storage_out_of_sync",
}

const onReject = (): void => {
DeviceListener.sharedInstance().dismissEncryptionSetup();
};

/**
* Show a toast prompting the user for some action related to setting up their encryption.
*
Expand All @@ -123,7 +123,7 @@ export const showToast = (kind: Kind): void => {
return;
}

const onAccept = async (): Promise<void> => {
const onPrimaryClick = async (): Promise<void> => {
if (kind === Kind.VERIFY_THIS_SESSION) {
Modal.createDialog(SetupEncryptionDialog, {}, undefined, /* priority = */ false, /* static = */ true);
} else {
Expand All @@ -142,16 +142,29 @@ export const showToast = (kind: Kind): void => {
}
};

const onSecondaryClick = (): void => {
if (kind === Kind.KEY_STORAGE_OUT_OF_SYNC) {
const payload: OpenToTabPayload = {
action: Action.ViewUserSettings,
initialTabId: UserTab.Encryption,
props: { showResetIdentity: true },
};
defaultDispatcher.dispatch(payload);
} else {
DeviceListener.sharedInstance().dismissEncryptionSetup();
}
};

ToastStore.sharedInstance().addOrReplaceToast({
key: TOAST_KEY,
title: getTitle(kind),
icon: getIcon(kind),
props: {
description: getDescription(kind),
primaryLabel: getSetupCaption(kind),
onPrimaryClick: onAccept,
onPrimaryClick,
secondaryLabel: getSecondaryButtonLabel(kind),
onSecondaryClick: onReject,
onSecondaryClick,
overrideWidth: kind === Kind.KEY_STORAGE_OUT_OF_SYNC ? "366px" : undefined,
},
component: GenericToast,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ describe("<UserSettingsDialog />", () => {

let sdkContext: SdkContextClass;
const defaultProps = { onFinished: jest.fn() };
const getComponent = (props: Partial<typeof defaultProps & { initialTabId?: UserTab }> = {}): ReactElement => (
<UserSettingsDialog sdkContext={sdkContext} {...defaultProps} {...props} />
);
const getComponent = (
props: Partial<typeof defaultProps & { initialTabId?: UserTab; props: Record<string, any> }> = {},
): ReactElement => <UserSettingsDialog sdkContext={sdkContext} {...defaultProps} {...props} />;

beforeEach(() => {
jest.clearAllMocks();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("<ResetIdentityPanel />", () => {

const onFinish = jest.fn();
const { asFragment } = render(
<ResetIdentityPanel onFinish={onFinish} onCancelClick={jest.fn()} />,
<ResetIdentityPanel variant="compromised" onFinish={onFinish} onCancelClick={jest.fn()} />,
withClientContextRenderOptions(matrixClient),
);
expect(asFragment()).toMatchSnapshot();
Expand All @@ -34,4 +34,13 @@ describe("<ResetIdentityPanel />", () => {
expect(matrixClient.getCrypto()!.resetEncryption).toHaveBeenCalled();
expect(onFinish).toHaveBeenCalled();
});

it("should display the 'forgot recovery key' variant correctly", async () => {
const onFinish = jest.fn();
const { asFragment } = render(
<ResetIdentityPanel variant="forgot" onFinish={onFinish} onCancelClick={jest.fn()} />,
withClientContextRenderOptions(matrixClient),
);
expect(asFragment()).toMatchSnapshot();
});
});
Loading
Loading