feat(settings): one public address (domain / DynDNS) for all mobile profiles

Moves the public address from each driver's share panel to Settings ->
System & backup, with Save and Detect public IP. The share panel shows
the address in use and links to the setting; empty keeps using the
local network address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
larssand
2026-10-11 19:45:53 +02:00
parent 42e53fd356
commit 533f59adf9
13 changed files with 121 additions and 53 deletions

View File

@@ -6,7 +6,7 @@ import assert from "node:assert/strict";
// load, and - the actual bug class this guards against - keep the selection when the page
// re-renders in place (e.g. after saving the form), not silently reset back to Connection.
// activeSettingsTab is a module-level variable specifically so it survives that re-render.
export async function runSettingsTabsSmoke({ evaluate, navigate, waitFor }) {
export async function runSettingsTabsSmoke({ evaluate, navigate, waitFor, savedState }) {
await navigate("settings", "settingsForm");
const activeTab = () => evaluate(`document.querySelector(".settings-tab.active")?.dataset.settingsTab`);
@@ -65,4 +65,22 @@ export async function runSettingsTabsSmoke({ evaluate, navigate, waitFor }) {
await waitFor(async () => (await activeTab()) === "system", "update link lands on System & backup");
assert.equal(await evaluate(`Boolean(document.getElementById("settingsUpdateSection")) && !document.getElementById("settingsUpdateSection").closest("[data-settings-panel]").hidden`), true, "the update section is visible");
console.log("PASS the sidebar update link opens Settings on System & backup -> Updates");
// One public address (domain / DynDNS) for every driver's mobile profile, set here.
const status = () => evaluate(`document.getElementById("settingsPublicAddressStatus").textContent`);
await evaluate(`(() => { document.getElementById("settingsPublicBaseUrl").value = "ftp://nope"; document.getElementById("settingsPublicBaseSave").click(); })()`);
await waitFor(async () => /complete http|komplett http/i.test(await status()), "an invalid address is refused");
await evaluate(`(() => { document.getElementById("settingsPublicBaseUrl").value = "https://klubben.dyndns.org:8443/some/path"; document.getElementById("settingsPublicBaseSave").click(); })()`);
await waitFor(async () => (await savedState()).settings.publicBaseUrl === "https://klubben.dyndns.org:8443", "saved as the origin, port kept");
await waitFor(async () => (await status()).includes("https://klubben.dyndns.org:8443"), "shown as in use");
await navigate("training", "view");
await waitFor(async () => Boolean(await evaluate(`document.querySelector("#training-overview-driver option:nth-child(2), #training-driver option:nth-child(2)")`)), "a driver to open");
await evaluate(`(() => { const select = document.getElementById("training-overview-driver") || document.getElementById("training-driver"); select.value = select.options[1].value; select.dispatchEvent(new Event("change")); })()`);
await waitFor(async () => (await evaluate(`document.querySelector(".training-public-address")?.textContent || ""`)).includes("https://klubben.dyndns.org:8443"), "the driver's share panel shows the address");
await evaluate(`document.getElementById("trainingPublicAddressSettings").click()`);
await waitFor(async () => (await activeTab()) === "system" && Boolean(await evaluate(`document.getElementById("settingsPublicBaseUrl")`)), "Change in Settings opens System");
await evaluate(`(() => { document.getElementById("settingsPublicBaseUrl").value = ""; document.getElementById("settingsPublicBaseSave").click(); })()`);
await waitFor(async () => (await savedState()).settings.publicBaseUrl === "", "cleared again for the rest of the suite");
console.log("PASS one public address for all mobile profiles is set under Settings and shown in the share panel");
}

View File

@@ -152,7 +152,7 @@ try {
await runTeamMembershipSmoke({ evaluate, navigate, fill, savedState, waitFor, pause, base });
await runEventManagerTabsSmoke({ evaluate, navigate, savedState, waitFor, base });
await runEventScheduleSmoke({ evaluate, navigate, fill, savedState, waitFor, base });
await runSettingsTabsSmoke({ evaluate, navigate, waitFor });
await runSettingsTabsSmoke({ evaluate, navigate, waitFor, savedState });
await runGuideTabsSmoke({ evaluate, navigate });
await runAuthSmoke({ evaluate, navigate, fill, savedState, waitFor });
await runTtsSettingsSmoke({ evaluate, navigate, savedState, waitFor });