fix(connections): allow toggling HTTPS off in Add Connection dialog
Push to Main CI / build-and-package (push) Canceled after 0s

The 'Use HTTPS' switch was disabled whenever the web client itself was
served over HTTPS, making it impossible to add a plain-HTTP node.
Keep the switch always interactive and instead show a mixed-content
warning when HTTP is selected on an HTTPS-served page.

Adds unit tests for the protocol toggle.
This commit is contained in:
2026-08-30 20:46:39 +00:00
parent cffd35fe05
commit 9481b6614a
3 changed files with 63 additions and 3 deletions
@@ -79,6 +79,7 @@
"inputPlaceholder": "192.168.1.10 or meshtastic.local",
"heading": "URL or IP",
"useHttps": "Use HTTPS",
"mixedContentWarning": "This page is loaded over HTTPS. Your browser may block connections to an HTTP node.",
"invalidUrl": {
"title": "Invalid URL",
"description": "Please enter a valid HTTP or HTTPS URL."
@@ -0,0 +1,53 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import AddConnectionDialog from "./AddConnectionDialog.tsx";
vi.mock("@core/hooks/useToast.ts", () => ({
useToast: () => ({ toast: vi.fn() }),
}));
vi.mock("@app/pages/Connections/utils", () => ({
testHttpReachable: vi.fn(() => Promise.resolve(true)),
}));
const getSwitch = () => screen.getByRole("switch");
describe("AddConnectionDialog HTTPS toggle", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("toggles between http and https when the app is served over http", () => {
render(<AddConnectionDialog open={true} isHTTPS={false} />);
const sw = getSwitch();
expect(sw).toHaveAttribute("aria-checked", "false");
expect(sw).toBeEnabled();
fireEvent.click(sw);
expect(sw).toHaveAttribute("aria-checked", "true");
fireEvent.click(sw);
expect(sw).toHaveAttribute("aria-checked", "false");
});
it("allows switching to http even when the app itself is served over https", () => {
render(<AddConnectionDialog open={true} isHTTPS={true} />);
const sw = getSwitch();
expect(sw).toHaveAttribute("aria-checked", "true");
expect(sw).toBeEnabled();
fireEvent.click(sw);
expect(sw).toHaveAttribute("aria-checked", "false");
});
it("keeps the protocol in sync with the url input prefix", () => {
render(<AddConnectionDialog open={true} isHTTPS={false} />);
// prefix reflects the current protocol
expect(screen.getByText("http://")).toBeInTheDocument();
fireEvent.click(getSwitch());
expect(screen.getByText("https://")).toBeInTheDocument();
});
});
@@ -275,7 +275,6 @@ export default function AddConnectionDialog({
typeof navigator !== "undefined" && "bluetooth" in navigator;
const serialSupported =
typeof navigator !== "undefined" && "serial" in navigator;
const isURLHTTPS = isHTTPS;
const reset = useCallback(() => {
dispatch({ type: "RESET", payload: { isHTTPS } });
@@ -434,7 +433,6 @@ export default function AddConnectionDialog({
<div className="flex items-center gap-2 mt-1">
<Switch
value={state.protocol}
disabled={!!isURLHTTPS}
checked={state.protocol === "https"}
onCheckedChange={(value) => {
dispatch({
@@ -446,6 +444,14 @@ export default function AddConnectionDialog({
></Switch>
<Label>{t("addConnection.httpConnection.useHttps")}</Label>
</div>
{isHTTPS && state.protocol === "http" && (
<p className="text-xs text-amber-600 dark:text-amber-400">
{t(
"addConnection.httpConnection.mixedContentWarning",
"This page is loaded over HTTPS. Your browser may block connections to an HTTP node.",
)}
</p>
)}
<div className="flex items-center gap-2 mt-4">
<Button
variant="subtle"
@@ -589,7 +595,7 @@ export default function AddConnectionDialog({
state,
bluetoothSupported,
serialSupported,
isURLHTTPS,
isHTTPS,
handlePickBluetooth,
handlePickSerial,
handleTestHttp,