fix: stop connection screens crashing outside the connection log provider

This commit is contained in:
LukeGus
2026-08-19 15:09:17 -05:00
parent 0bef8b99b4
commit a15372a224
3 changed files with 60 additions and 5 deletions
@@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState } from "react";
import { useConnectionLog } from "@/ssh/connection-log/ConnectionLogContext.tsx";
import { useOptionalConnectionLog } from "@/ssh/connection-log/ConnectionLogContext.tsx";
import { useTranslation } from "react-i18next";
import { copyToClipboard } from "@/lib/clipboard.ts";
import { Button } from "@/components/button.tsx";
@@ -34,20 +34,21 @@ export function ConnectionLogPanel({
className,
}: ConnectionLogPanelProps) {
const { t } = useTranslation();
const connectionLog = useOptionalConnectionLog();
const { logs, clearLogs, isExpanded, toggleExpanded, setIsExpanded } =
useConnectionLog();
connectionLog ?? {};
const lastLogRef = useRef<HTMLDivElement>(null);
const [manuallyCollapsed, setManuallyCollapsed] = useState(false);
useEffect(() => {
if (hasConnectionError) {
if (hasConnectionError && setIsExpanded) {
setManuallyCollapsed(false);
setIsExpanded(true);
}
}, [hasConnectionError, setIsExpanded]);
useEffect(() => {
if (isConnected && !hasConnectionError && !isConnecting) {
if (isConnected && !hasConnectionError && !isConnecting && clearLogs) {
clearLogs();
setManuallyCollapsed(false);
}
@@ -60,7 +61,9 @@ export function ConnectionLogPanel({
}, [logs]);
const shouldShow =
!isConnected && (isConnecting || hasConnectionError || logs.length > 0);
!!connectionLog &&
!isConnected &&
(isConnecting || hasConnectionError || logs.length > 0);
if (!shouldShow) {
return null;
@@ -88,3 +88,9 @@ export function useConnectionLog() {
}
return context;
}
// Loading and error screens render outside any provider, so they get undefined
// instead of a crash.
export function useOptionalConnectionLog() {
return useContext(ConnectionLogContext);
}
@@ -0,0 +1,46 @@
import { describe, it, expect, vi, afterEach } from "vitest";
import { render, screen, cleanup } from "@testing-library/react";
vi.mock("react-i18next", () => ({
useTranslation: () => ({ t: (key: string) => key }),
}));
import { ConnectionScreen } from "../../../components/connection/ConnectionScreen";
import { ConnectionLogProvider } from "../../../ssh/connection-log/ConnectionLogContext";
afterEach(cleanup);
describe("ConnectionScreen", () => {
// Loading and host-not-found screens render before the provider is mounted,
// which used to throw and take down the whole RDP/VNC/Telnet tab.
it("renders without a ConnectionLogProvider", () => {
expect(() =>
render(<ConnectionScreen status="connecting" message="common.loading" />),
).not.toThrow();
expect(screen.getByText("common.loading")).toBeTruthy();
});
it("renders the disconnected state without a provider", () => {
expect(() =>
render(
<ConnectionScreen
status="disconnected"
message="guacamole.hostNotFound"
/>,
),
).not.toThrow();
expect(screen.getByText("guacamole.hostNotFound")).toBeTruthy();
});
it("still shows the connection log when a provider is present", () => {
render(
<ConnectionLogProvider>
<ConnectionScreen status="connecting" message="common.loading" />
</ConnectionLogProvider>,
);
expect(screen.getByText(/terminal\.connectionLogTitle/)).toBeTruthy();
});
});