fix(connections): allow toggling HTTPS off in Add Connection dialog
Push to Main CI / build-and-package (push) Canceled after 0s
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:
@@ -79,6 +79,7 @@
|
|||||||
"inputPlaceholder": "192.168.1.10 or meshtastic.local",
|
"inputPlaceholder": "192.168.1.10 or meshtastic.local",
|
||||||
"heading": "URL or IP",
|
"heading": "URL or IP",
|
||||||
"useHttps": "Use HTTPS",
|
"useHttps": "Use HTTPS",
|
||||||
|
"mixedContentWarning": "This page is loaded over HTTPS. Your browser may block connections to an HTTP node.",
|
||||||
"invalidUrl": {
|
"invalidUrl": {
|
||||||
"title": "Invalid URL",
|
"title": "Invalid URL",
|
||||||
"description": "Please enter a valid HTTP or HTTPS 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;
|
typeof navigator !== "undefined" && "bluetooth" in navigator;
|
||||||
const serialSupported =
|
const serialSupported =
|
||||||
typeof navigator !== "undefined" && "serial" in navigator;
|
typeof navigator !== "undefined" && "serial" in navigator;
|
||||||
const isURLHTTPS = isHTTPS;
|
|
||||||
|
|
||||||
const reset = useCallback(() => {
|
const reset = useCallback(() => {
|
||||||
dispatch({ type: "RESET", payload: { isHTTPS } });
|
dispatch({ type: "RESET", payload: { isHTTPS } });
|
||||||
@@ -434,7 +433,6 @@ export default function AddConnectionDialog({
|
|||||||
<div className="flex items-center gap-2 mt-1">
|
<div className="flex items-center gap-2 mt-1">
|
||||||
<Switch
|
<Switch
|
||||||
value={state.protocol}
|
value={state.protocol}
|
||||||
disabled={!!isURLHTTPS}
|
|
||||||
checked={state.protocol === "https"}
|
checked={state.protocol === "https"}
|
||||||
onCheckedChange={(value) => {
|
onCheckedChange={(value) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
@@ -446,6 +444,14 @@ export default function AddConnectionDialog({
|
|||||||
></Switch>
|
></Switch>
|
||||||
<Label>{t("addConnection.httpConnection.useHttps")}</Label>
|
<Label>{t("addConnection.httpConnection.useHttps")}</Label>
|
||||||
</div>
|
</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">
|
<div className="flex items-center gap-2 mt-4">
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
@@ -589,7 +595,7 @@ export default function AddConnectionDialog({
|
|||||||
state,
|
state,
|
||||||
bluetoothSupported,
|
bluetoothSupported,
|
||||||
serialSupported,
|
serialSupported,
|
||||||
isURLHTTPS,
|
isHTTPS,
|
||||||
handlePickBluetooth,
|
handlePickBluetooth,
|
||||||
handlePickSerial,
|
handlePickSerial,
|
||||||
handleTestHttp,
|
handleTestHttp,
|
||||||
|
|||||||
Reference in New Issue
Block a user