mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-05 19:59:20 +00:00
Improve error messages for sfu auth problems
This commit is contained in:
@@ -236,6 +236,7 @@
|
|||||||
"stop_video_button_label": "Stop video",
|
"stop_video_button_label": "Stop video",
|
||||||
"submitting": "Submitting…",
|
"submitting": "Submitting…",
|
||||||
"switch_camera": "Switch camera",
|
"switch_camera": "Switch camera",
|
||||||
|
"technical_details": "",
|
||||||
"unauthenticated_view_body": "Not registered yet? <2>Create an account</2>",
|
"unauthenticated_view_body": "Not registered yet? <2>Create an account</2>",
|
||||||
"unauthenticated_view_login_button": "Login to your account",
|
"unauthenticated_view_login_button": "Login to your account",
|
||||||
"unauthenticated_view_ssla_caption": "By clicking \"Go\", you agree to our <2>Software and Services License Agreement (SSLA)</2>",
|
"unauthenticated_view_ssla_caption": "By clicking \"Go\", you agree to our <2>Software and Services License Agreement (SSLA)</2>",
|
||||||
|
|||||||
@@ -20,3 +20,21 @@
|
|||||||
color: var(--cpd-color-text-secondary);
|
color: var(--cpd-color-text-secondary);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.technicalDetails {
|
||||||
|
margin-top: var(--cpd-space-1x);
|
||||||
|
}
|
||||||
|
|
||||||
|
.technicalDetailsSummary {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technicalDetailsPre {
|
||||||
|
margin-top: var(--cpd-space-2x);
|
||||||
|
padding: var(--cpd-space-2x);
|
||||||
|
background-color: var(--cpd-color-bg-subtle-secondary);
|
||||||
|
overflow: auto;
|
||||||
|
font-size: var(--cpd-font-size-body-sm);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
vitest,
|
vitest,
|
||||||
} from "vitest";
|
} from "vitest";
|
||||||
import fetchMock from "fetch-mock";
|
import fetchMock from "fetch-mock";
|
||||||
|
import { MatrixError } from "matrix-js-sdk";
|
||||||
|
|
||||||
import { getSFUConfigWithOpenID, type OpenIDClientParts } from "./openIDSFU";
|
import { getSFUConfigWithOpenID, type OpenIDClientParts } from "./openIDSFU";
|
||||||
import { testJWTToken } from "../utils/test-fixtures";
|
import { testJWTToken } from "../utils/test-fixtures";
|
||||||
@@ -63,7 +64,10 @@ describe("getSFUConfigWithOpenID", () => {
|
|||||||
fetchMock.post("https://sfu.example.org/sfu/get", () => {
|
fetchMock.post("https://sfu.example.org/sfu/get", () => {
|
||||||
return {
|
return {
|
||||||
status: 500,
|
status: 500,
|
||||||
body: { error: "Test failure" },
|
body: {
|
||||||
|
errcode: "M_LOOKUP_FAILED",
|
||||||
|
error: "Failed to look up user info from homeserver",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
@@ -75,9 +79,12 @@ describe("getSFUConfigWithOpenID", () => {
|
|||||||
);
|
);
|
||||||
} catch (ex: unknown) {
|
} catch (ex: unknown) {
|
||||||
expect(ex).toBeInstanceOf(FailToGetOpenIdToken);
|
expect(ex).toBeInstanceOf(FailToGetOpenIdToken);
|
||||||
expect((ex as FailToGetOpenIdToken).cause).toEqual(
|
expect((ex as FailToGetOpenIdToken).cause).toBeInstanceOf(MatrixError);
|
||||||
new Error("SFU Config fetch failed with status code 500"),
|
const mxError = (ex as Error).cause as MatrixError;
|
||||||
|
expect(mxError.message).toEqual(
|
||||||
|
"MatrixError: [500] Failed to look up user info from homeserver",
|
||||||
);
|
);
|
||||||
|
|
||||||
void (await fetchMock.flush());
|
void (await fetchMock.flush());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -181,13 +188,19 @@ describe("getSFUConfigWithOpenID", () => {
|
|||||||
fetchMock.post("https://sfu.example.org/get_token", () => {
|
fetchMock.post("https://sfu.example.org/get_token", () => {
|
||||||
return {
|
return {
|
||||||
status: 500,
|
status: 500,
|
||||||
body: { error: "Test failure" },
|
body: {
|
||||||
|
errcode: "M_LOOKUP_FAILED",
|
||||||
|
error: "Failed to look up user info from homeserver",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
fetchMock.post("https://sfu.example.org/sfu/get", () => {
|
fetchMock.post("https://sfu.example.org/sfu/get", () => {
|
||||||
return {
|
return {
|
||||||
status: 500,
|
status: 500,
|
||||||
body: { error: "Test failure" },
|
body: {
|
||||||
|
errcode: "M_LOOKUP_FAILED",
|
||||||
|
error: "Failed to look up user info from homeserver",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
@@ -203,8 +216,10 @@ describe("getSFUConfigWithOpenID", () => {
|
|||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
expect(ex).toBeInstanceOf(FailToGetOpenIdToken);
|
expect(ex).toBeInstanceOf(FailToGetOpenIdToken);
|
||||||
expect((ex as FailToGetOpenIdToken).cause).toEqual(
|
expect((ex as FailToGetOpenIdToken).cause).toBeInstanceOf(MatrixError);
|
||||||
new Error("SFU Config fetch failed with status code 500"),
|
const mxError = (ex as Error).cause as MatrixError;
|
||||||
|
expect(mxError.message).toEqual(
|
||||||
|
"MatrixError: [500] Failed to look up user info from homeserver",
|
||||||
);
|
);
|
||||||
void (await fetchMock.flush());
|
void (await fetchMock.flush());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type IOpenIDToken, type MatrixClient } from "matrix-js-sdk";
|
import {
|
||||||
|
type IOpenIDToken,
|
||||||
|
type MatrixClient,
|
||||||
|
parseErrorResponse,
|
||||||
|
} from "matrix-js-sdk";
|
||||||
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
|
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
|
||||||
import { type Logger } from "matrix-js-sdk/lib/logger";
|
import { type Logger } from "matrix-js-sdk/lib/logger";
|
||||||
|
|
||||||
@@ -248,7 +252,7 @@ async function getLiveKitJWT(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error("SFU Config fetch failed with status code " + res.status);
|
throw parseErrorResponse(res, await res.text());
|
||||||
}
|
}
|
||||||
return await res.json();
|
return await res.json();
|
||||||
}
|
}
|
||||||
@@ -308,7 +312,7 @@ export async function getLiveKitJWTWithDelayDelegation(
|
|||||||
if (res.status === 404) {
|
if (res.status === 404) {
|
||||||
throw new NotSupportedError(msg);
|
throw new NotSupportedError(msg);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(msg);
|
throw parseErrorResponse(res, await res.text());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await res.json();
|
return await res.json();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
} from "react";
|
} from "react";
|
||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { MatrixError } from "matrix-js-sdk";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type CallErrorRecoveryAction,
|
type CallErrorRecoveryAction,
|
||||||
@@ -25,6 +26,7 @@ import {
|
|||||||
ConnectionLostError,
|
ConnectionLostError,
|
||||||
E2EENotSupportedError,
|
E2EENotSupportedError,
|
||||||
type ElementCallError,
|
type ElementCallError,
|
||||||
|
FailToGetOpenIdToken,
|
||||||
InsufficientCapacityError,
|
InsufficientCapacityError,
|
||||||
MatrixRTCTransportMissingError,
|
MatrixRTCTransportMissingError,
|
||||||
UnknownCallError,
|
UnknownCallError,
|
||||||
@@ -252,3 +254,69 @@ test("should have a close button in widget mode", async () => {
|
|||||||
);
|
);
|
||||||
expect(mockWidget.api.transport.stop).toHaveBeenCalled();
|
expect(mockWidget.api.transport.stop).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should show technical details when error has a matrixError cause", async () => {
|
||||||
|
const underlyingError = new MatrixError(
|
||||||
|
{
|
||||||
|
errcode: "M_LOOKUP_FAILED",
|
||||||
|
error: "Failed to look up user info from homeserver",
|
||||||
|
},
|
||||||
|
500,
|
||||||
|
"https://matrix-rtc.m.localhost/livekit/jwt/sfu/get",
|
||||||
|
);
|
||||||
|
const error = new FailToGetOpenIdToken(underlyingError);
|
||||||
|
|
||||||
|
const TestComponent = (): ReactNode => {
|
||||||
|
throw error;
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<BrowserRouter>
|
||||||
|
<GroupCallErrorBoundary
|
||||||
|
onError={vi.fn()}
|
||||||
|
recoveryActionHandler={vi.fn()}
|
||||||
|
widget={null}
|
||||||
|
>
|
||||||
|
<TestComponent />
|
||||||
|
</GroupCallErrorBoundary>
|
||||||
|
</BrowserRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await screen.findByText("Something went wrong");
|
||||||
|
|
||||||
|
// Technical details should be present
|
||||||
|
const detailsElement = screen.getByText("Technical details");
|
||||||
|
expect(detailsElement).toBeInTheDocument();
|
||||||
|
|
||||||
|
// Verify error details are shown
|
||||||
|
expect(
|
||||||
|
screen.getByText(/Failed to look up user info from homeserver/i, {
|
||||||
|
selector: "pre",
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should not show technical details when error has no matrix error cause", async () => {
|
||||||
|
const error = new ConnectionLostError();
|
||||||
|
|
||||||
|
const TestComponent = (): ReactNode => {
|
||||||
|
throw error;
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<BrowserRouter>
|
||||||
|
<GroupCallErrorBoundary
|
||||||
|
onError={vi.fn()}
|
||||||
|
recoveryActionHandler={vi.fn()}
|
||||||
|
widget={null}
|
||||||
|
>
|
||||||
|
<TestComponent />
|
||||||
|
</GroupCallErrorBoundary>
|
||||||
|
</BrowserRouter>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await screen.findByText("Connection lost");
|
||||||
|
|
||||||
|
// Technical details should not be present (ConnectionLostError has no cause)
|
||||||
|
expect(screen.queryByText("Technical details")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
import { Button } from "@vector-im/compound-web";
|
import { Button } from "@vector-im/compound-web";
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
|
import { MatrixError } from "matrix-js-sdk";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ConnectionLostError,
|
ConnectionLostError,
|
||||||
@@ -34,6 +35,7 @@ import {
|
|||||||
import { FullScreenView } from "../FullScreenView.tsx";
|
import { FullScreenView } from "../FullScreenView.tsx";
|
||||||
import { ErrorView } from "../ErrorView.tsx";
|
import { ErrorView } from "../ErrorView.tsx";
|
||||||
import { type WidgetHelpers } from "../widget.ts";
|
import { type WidgetHelpers } from "../widget.ts";
|
||||||
|
import styles from "../ErrorView.module.css";
|
||||||
|
|
||||||
export type CallErrorRecoveryAction = "reconnect"; // | "retry" ;
|
export type CallErrorRecoveryAction = "reconnect"; // | "retry" ;
|
||||||
|
|
||||||
@@ -78,6 +80,9 @@ const ErrorPage: FC<ErrorPageProps> = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const technicalError =
|
||||||
|
error.cause instanceof MatrixError ? error.cause : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FullScreenView>
|
<FullScreenView>
|
||||||
<ErrorView
|
<ErrorView
|
||||||
@@ -95,6 +100,16 @@ const ErrorPage: FC<ErrorPageProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
{technicalError ? (
|
||||||
|
<details className={styles.technicalDetails}>
|
||||||
|
<summary className={styles.technicalDetailsSummary}>
|
||||||
|
{t("technical_details")}
|
||||||
|
</summary>
|
||||||
|
<pre className={styles.technicalDetailsPre}>
|
||||||
|
{technicalError.message}
|
||||||
|
</pre>
|
||||||
|
</details>
|
||||||
|
) : null}
|
||||||
{actions &&
|
{actions &&
|
||||||
actions.map((action, index) => (
|
actions.map((action, index) => (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -260,7 +260,10 @@ describe("Start connection states", () => {
|
|||||||
await deferredSFU.promise;
|
await deferredSFU.promise;
|
||||||
return {
|
return {
|
||||||
status: 500,
|
status: 500,
|
||||||
body: "Internal Server Error",
|
body: {
|
||||||
|
errcode: "M_LOOKUP_FAILED",
|
||||||
|
error: "Failed to look up user info from homeserver",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -282,7 +285,7 @@ describe("Start connection states", () => {
|
|||||||
capturedState.cause instanceof Error
|
capturedState.cause instanceof Error
|
||||||
) {
|
) {
|
||||||
expect(capturedState.cause.message).toContain(
|
expect(capturedState.cause.message).toContain(
|
||||||
"SFU Config fetch failed with status code 500",
|
"Failed to look up user info from homeserver",
|
||||||
);
|
);
|
||||||
expect(connection.transport.livekit_alias).toEqual(
|
expect(connection.transport.livekit_alias).toEqual(
|
||||||
livekitFocus.livekit_alias,
|
livekitFocus.livekit_alias,
|
||||||
|
|||||||
Reference in New Issue
Block a user