diff --git a/packages/app/e2e/browser/settings-navigation.spec.ts b/packages/app/e2e/browser/settings-navigation.spec.ts index bc8e1eedec..8224132d71 100644 --- a/packages/app/e2e/browser/settings-navigation.spec.ts +++ b/packages/app/e2e/browser/settings-navigation.spec.ts @@ -27,6 +27,8 @@ import { openCompactSettingsHost, expectAddHostMethodOptions, fillDirectHostUri, + fillDirectHostFields, + connectDirectHost, expectDirectHostFormValues, expectDirectHostSslEnabled, expectDirectHostUriValue, @@ -112,6 +114,36 @@ test.describe("Settings sidebar navigation", () => { await expectDirectHostUriHidden(page); }); + test("direct connection connects to the open Advanced URI when Host is blank", async ({ + page, + }) => { + await gotoAppShell(page); + await openSettings(page); + await openAddHostFlow(page); + await selectHostConnectionType(page, "direct"); + + await toggleHostAdvanced(page); + await fillDirectHostUri(page, `tcp://127.0.0.1:${getE2EDaemonPort()}`); + + await connectDirectHost(page); + }); + + test("direct connection connects to an edited Advanced URI instead of the fields", async ({ + page, + }) => { + await gotoAppShell(page); + await openSettings(page); + await openAddHostFlow(page); + await selectHostConnectionType(page, "direct"); + + await fillDirectHostFields(page, { host: "127.0.0.1", port: "9" }); + await toggleHostAdvanced(page); + await expectDirectHostUriValue(page, "tcp://127.0.0.1:9"); + await fillDirectHostUri(page, `tcp://127.0.0.1:${getE2EDaemonPort()}`); + + await connectDirectHost(page); + }); + test("Escape lets settings dropdowns and modals close before leaving settings", async ({ page, }) => { diff --git a/packages/app/e2e/support/helpers/settings.ts b/packages/app/e2e/support/helpers/settings.ts index 1702f7fe44..db0f0b3bd3 100644 --- a/packages/app/e2e/support/helpers/settings.ts +++ b/packages/app/e2e/support/helpers/settings.ts @@ -253,6 +253,19 @@ export async function fillDirectHostUri(page: Page, uri: string): Promise await page.getByTestId("direct-host-uri-input").fill(uri); } +export async function fillDirectHostFields( + page: Page, + fields: { host: string; port: string }, +): Promise { + await page.getByTestId("direct-host-input").fill(fields.host); + await page.getByTestId("direct-port-input").fill(fields.port); +} + +export async function connectDirectHost(page: Page): Promise { + await page.getByTestId("direct-host-submit").click(); + await expect(page.getByTestId("add-host-modal")).toHaveCount(0, { timeout: 30_000 }); +} + export async function expectDirectHostFormValues( page: Page, fields: { host: string; port: string; password: string }, diff --git a/packages/app/src/components/add-host-modal.tsx b/packages/app/src/components/add-host-modal.tsx index b388d76047..fb81d73d12 100644 --- a/packages/app/src/components/add-host-modal.tsx +++ b/packages/app/src/components/add-host-modal.tsx @@ -195,6 +195,12 @@ function draftFromConnectionUri(uri: string): DirectConnectionDraft { }; } +// The Password field stays visible under Advanced, so it fills in a URI that carries none. +function draftFromAdvancedUri(uri: string, fieldPassword: string): DirectConnectionDraft { + const draft = draftFromConnectionUri(uri); + return draft.password ? draft : { ...draft, password: fieldPassword }; +} + function normalizeTransportMessage(message: string | null | undefined): string | null { if (!message) return null; const trimmed = message.trim(); @@ -405,18 +411,18 @@ export function AddHostModal({ visible, onClose, onCancel, onSaved }: AddHostMod const handleSave = useCallback(async () => { if (isSaving) return; - const relayUri = isAdvancedOpen ? advancedUri.trim() : ""; - if (relayUri.startsWith("relay://") || relayUri.includes("#connect=")) { - await handleSaveRelay(relayUri); + const advancedInput = isAdvancedOpen ? advancedUri.trim() : ""; + if (advancedInput.startsWith("relay://") || advancedInput.includes("#connect=")) { + await handleSaveRelay(advancedInput); return; } let connection: PreparedDirectConnection; try { - connection = prepareDirectConnection( - { host, port, useTls, password }, - directConnectionLabels, - ); + const draft = advancedInput + ? draftFromAdvancedUri(advancedInput, password) + : { host, port, useTls, password }; + connection = prepareDirectConnection(draft, directConnectionLabels); } catch (error) { const message = error instanceof Error ? error.message : directConnectionLabels.invalidConnection;