Rename callback on E2eSetup component (#31274)

* Rename callback on E2eSetup component

`BaseDialog.onFinished` is unused when `hasCancel=false`, so this callback is
only used when the user clicks cancel. For clarity, rename it.

* Test for cancellation behaviour
This commit is contained in:
Richard van der Hoff
2025-11-20 18:17:51 +00:00
committed by GitHub
parent 64130a018b
commit c203f02731
5 changed files with 52 additions and 19 deletions

View File

@@ -2101,7 +2101,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} else if (this.state.view === Views.COMPLETE_SECURITY) {
view = <CompleteSecurity onFinished={this.onCompleteSecurityE2eSetupFinished} />;
} else if (this.state.view === Views.E2E_SETUP) {
view = <E2eSetup onFinished={this.onCompleteSecurityE2eSetupFinished} />;
view = <E2eSetup onCancelled={this.onCompleteSecurityE2eSetupFinished} />;
} else if (this.state.view === Views.LOGGED_IN) {
// `ready` and `view==LOGGED_IN` may be set before `page_type` (because the
// latter is set via the dispatcher). If we don't yet have a `page_type`,

View File

@@ -13,15 +13,19 @@ import CompleteSecurityBody from "../../views/auth/CompleteSecurityBody";
import { InitialCryptoSetupDialog } from "../../views/dialogs/security/InitialCryptoSetupDialog";
interface IProps {
onFinished: () => void;
/** Callback which is called if the crypto setup failed, and the user clicked the 'cancel' button */
onCancelled: () => void;
}
/**
* An {@link AuthPage} which shows the {@link InitialCryptoSetupDialog}.
*/
export default class E2eSetup extends React.Component<IProps> {
public render(): React.ReactNode {
return (
<AuthPage>
<CompleteSecurityBody>
<InitialCryptoSetupDialog onFinished={this.props.onFinished} />
<InitialCryptoSetupDialog onCancelled={this.props.onCancelled} />
</CompleteSecurityBody>
</AuthPage>
);

View File

@@ -16,23 +16,21 @@ import Spinner from "../../elements/Spinner";
import { InitialCryptoSetupStore, useInitialCryptoSetupStatus } from "../../../../stores/InitialCryptoSetupStore";
interface Props {
onFinished: (success?: boolean) => void;
/** Callback which is called if the crypto setup failed, and the user clicked the 'cancel' button */
onCancelled: () => void;
}
/*
* Walks the user through the process of creating a cross-signing keys.
/**
* Walks the user through the process of creating cross-signing keys.
*
* In most cases, only a spinner is shown, but for more
* complex auth like SSO, the user may need to complete some steps to proceed.
*/
export const InitialCryptoSetupDialog: React.FC<Props> = ({ onFinished }) => {
export const InitialCryptoSetupDialog: React.FC<Props> = ({ onCancelled }) => {
const onRetryClick = useCallback(() => {
InitialCryptoSetupStore.sharedInstance().retry();
}, []);
const onCancelClick = useCallback(() => {
onFinished(false);
}, [onFinished]);
const status = useInitialCryptoSetupStatus(InitialCryptoSetupStore.sharedInstance());
let content;
@@ -44,7 +42,7 @@ export const InitialCryptoSetupDialog: React.FC<Props> = ({ onFinished }) => {
<DialogButtons
primaryButton={_t("action|retry")}
onPrimaryButtonClick={onRetryClick}
onCancel={onCancelClick}
onCancel={onCancelled}
/>
</div>
</div>
@@ -60,7 +58,6 @@ export const InitialCryptoSetupDialog: React.FC<Props> = ({ onFinished }) => {
return (
<BaseDialog
className="mx_CreateCrossSigningDialog"
onFinished={onFinished}
title={_t("encryption|bootstrap_title")}
hasCancel={false}
fixedWidth={false}

View File

@@ -0,0 +1,35 @@
/*
Copyright 2025 Element Creations 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 React from "react";
import { render } from "jest-matrix-react";
import { mocked } from "jest-mock";
import E2eSetup from "../../../../../src/components/structures/auth/E2eSetup.tsx";
import { InitialCryptoSetupStore } from "../../../../../src/stores/InitialCryptoSetupStore.ts";
afterEach(() => jest.restoreAllMocks());
describe("LeftPanel", () => {
it("should call `onCancelled` when the user clicks the cancel button", () => {
const mockInitialCryptoSetupStore = {
getStatus: jest.fn(),
on: jest.fn(),
off: jest.fn(),
};
jest.spyOn(InitialCryptoSetupStore, "sharedInstance").mockReturnValue(mockInitialCryptoSetupStore as any);
// We need the setup process to have failed, for the dialog to present a cancel button.
mocked(mockInitialCryptoSetupStore.getStatus).mockReturnValue("error");
const onCancelled = jest.fn();
const { getByRole } = render(<E2eSetup onCancelled={onCancelled} />);
getByRole("button", { name: "Cancel" }).click();
expect(onCancelled).toHaveBeenCalled();
});
});

View File

@@ -31,11 +31,9 @@ describe("InitialCryptoSetupDialog", () => {
});
it("should show a spinner while the setup is in progress", async () => {
const onFinished = jest.fn();
storeMock.getStatus.mockReturnValue("in_progress");
render(<InitialCryptoSetupDialog onFinished={onFinished} />);
render(<InitialCryptoSetupDialog onCancelled={jest.fn()} />);
expect(screen.getByTestId("spinner")).toBeInTheDocument();
});
@@ -43,16 +41,15 @@ describe("InitialCryptoSetupDialog", () => {
it("should display an error if setup has failed", async () => {
storeMock.getStatus.mockReturnValue("error");
render(<InitialCryptoSetupDialog onFinished={jest.fn()} />);
render(<InitialCryptoSetupDialog onCancelled={jest.fn()} />);
await expect(await screen.findByRole("button", { name: "Retry" })).toBeInTheDocument();
});
it("calls retry when retry button pressed", async () => {
const onFinished = jest.fn();
storeMock.getStatus.mockReturnValue("error");
render(<InitialCryptoSetupDialog onFinished={onFinished} />);
render(<InitialCryptoSetupDialog onCancelled={jest.fn()} />);
await userEvent.click(await screen.findByRole("button", { name: "Retry" }));