diff --git a/locales/en/app.json b/locales/en/app.json index 5be1bfff6..c98a8d9f5 100644 --- a/locales/en/app.json +++ b/locales/en/app.json @@ -88,6 +88,7 @@ }, "mute_all_audio": "Mute all audio (participants, reactions, join sounds)", "show_connection_stats": "Show connection statistics", + "throw_error_button": "Throw test error", "url_params": "URL parameters" }, "disconnected_banner": "Connectivity to the server has been lost.", diff --git a/src/settings/DeveloperSettingsTab.test.tsx b/src/settings/DeveloperSettingsTab.test.tsx index 6cd645faf..5fdcbda34 100644 --- a/src/settings/DeveloperSettingsTab.test.tsx +++ b/src/settings/DeveloperSettingsTab.test.tsx @@ -30,6 +30,9 @@ import { } from "@element-hq/matrixrtc-sdk"; import type * as MatrixRTCSdk from "@element-hq/matrixrtc-sdk"; import { exampleTransport, mockConfig } from "../utils/test"; +import { GroupCallErrorBoundary } from "../room/GroupCallErrorBoundary"; +import { LeaveToHomeProvider } from "../LeaveToHomeContext"; +import { ElementCallError, ErrorCode } from "../utils/errors"; // Mock url params hook to avoid environment-dependent snapshot churn. vi.mock("../UrlParams", () => ({ @@ -493,4 +496,38 @@ describe("DeveloperSettingsTab", () => { ); }); }); + describe("throw test error", () => { + it("throws an ElementCallError that the error boundary shows", async () => { + const user = userEvent.setup(); + const client = createMockMatrixClient(); + const onError = vi.fn(); + + render( + + + + + + + , + ); + + await user.click( + screen.getByRole("button", { name: "Throw test error" }), + ); + + expect(await screen.findByText("Something went wrong")).toBeVisible(); + expect(onError).toHaveBeenCalledOnce(); + const error = onError.mock.calls[0][0] as unknown; + expect(error).toBeInstanceOf(ElementCallError); + expect((error as ElementCallError).code).toBe(ErrorCode.UNKNOWN_ERROR); + }); + }); }); diff --git a/src/settings/DeveloperSettingsTab.tsx b/src/settings/DeveloperSettingsTab.tsx index a2bbd9380..05aad372b 100644 --- a/src/settings/DeveloperSettingsTab.tsx +++ b/src/settings/DeveloperSettingsTab.tsx @@ -22,6 +22,7 @@ import { import { logger } from "matrix-js-sdk/lib/logger"; import { type UnstableLivekitTransport } from "matrix-js-sdk/lib/matrixrtc"; import { + Button, EditInPlace, ErrorMessage, Root as Form, @@ -72,6 +73,7 @@ import { } from "@element-hq/matrixrtc-sdk"; import { useBehavior } from "../useBehavior"; import { type ViewModel } from "../state/ViewModel.ts"; +import { type ElementCallError, UnknownCallError } from "../utils/errors.ts"; /** * The state of MatrixRTC's media key rotation. @@ -189,6 +191,22 @@ export const DeveloperSettingsTab: FC = ({ const urlParams = useUrlParams(); + // An error thrown from a click handler never reaches an error boundary, so + // the button only records it and the next render throws it instead. + const [errorToThrow, setErrorToThrow] = useState( + null, + ); + const throwTestError = useCallback( + () => + setErrorToThrow( + new UnknownCallError( + new Error("Test error thrown from the developer settings"), + ), + ), + [], + ); + if (errorToThrow !== null) throw errorToThrow; + const MediaQualitySettings: React.FC<{ id: string; header: string; @@ -649,6 +667,10 @@ export const DeveloperSettingsTab: FC = ({ + +

{t("developer_mode.environment_variables")}

{JSON.stringify(env, null, 2)}

{t("developer_mode.url_params")}

diff --git a/src/settings/__snapshots__/DeveloperSettingsTab.test.tsx.snap b/src/settings/__snapshots__/DeveloperSettingsTab.test.tsx.snap index a2e8671c1..b03197415 100644 --- a/src/settings/__snapshots__/DeveloperSettingsTab.test.tsx.snap +++ b/src/settings/__snapshots__/DeveloperSettingsTab.test.tsx.snap @@ -635,6 +635,21 @@ exports[`DeveloperSettingsTab > renders and matches snapshot 1`] = ` data-orientation="horizontal" role="separator" /> + +