From c829a42a45a531567c575efe6ddf848f6ffe3c76 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 21 Jun 2026 21:07:00 -0400 Subject: [PATCH] fix(wallet): testing the connection no longer wipes an unsaved typed URI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Test connection" then "Save" removed the wallet. handleWalletTest re-rendered the pane (refreshWalletPaneState) in its finally, which repopulates the form from SAVED settings — empty for a URI you typed but hadn't saved — wiping the field. The next Save then saw an empty field and took the "removed" branch. Test must preserve what the user entered: refresh only the button state (applyWalletControlState), not the whole form. tests/wallet-test-preserves-input.test.mjs: a Test of an unsaved typed URI keeps it in the field. Mutation-verified (restoring refreshWalletPaneState fails it). Co-Authored-By: Claude Opus 4.8 --- js/ui/profileModal/ProfileWalletController.js | 6 +- tests/wallet-test-preserves-input.test.mjs | 56 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/wallet-test-preserves-input.test.mjs diff --git a/js/ui/profileModal/ProfileWalletController.js b/js/ui/profileModal/ProfileWalletController.js index 018f109e..db663aee 100644 --- a/js/ui/profileModal/ProfileWalletController.js +++ b/js/ui/profileModal/ProfileWalletController.js @@ -764,7 +764,11 @@ export class ProfileWalletController { this.mainController.showError(detail); } finally { this.setWalletPaneBusy(false); - this.refreshWalletPaneState(); + // Do NOT refreshWalletPaneState() here: it re-renders the form from SAVED + // settings and would wipe a URI the user typed but hasn't saved yet (then a + // subsequent Save sees an empty field and "removes" the wallet). Testing + // must preserve the entered value — only refresh the button state. + this.applyWalletControlState(); if (finalStatus) { this.updateWalletStatus(finalStatus, finalVariant); } diff --git a/tests/wallet-test-preserves-input.test.mjs b/tests/wallet-test-preserves-input.test.mjs new file mode 100644 index 00000000..ef196f5b --- /dev/null +++ b/tests/wallet-test-preserves-input.test.mjs @@ -0,0 +1,56 @@ +// Regression: testing the wallet connection must NOT wipe a URI the user typed +// but hasn't saved yet. handleWalletTest used to refreshWalletPaneState() in its +// finally, which re-renders the form from SAVED settings (empty for an unsaved +// URI) and cleared the field — so a subsequent Save saw an empty field and +// "removed" the wallet. + +import "./test-helpers/setup-localstorage.mjs"; +import assert from "node:assert/strict"; +import test from "node:test"; +import { ProfileWalletController } from "../js/ui/profileModal/ProfileWalletController.js"; + +const PUBKEY = "a".repeat(64); +const TYPED_URI = "nostr+walletconnect://abc?relay=wss://r.example&secret=def"; + +// isSecretInputElement accepts any object with a string `value`, so a plain +// object is a faithful stand-in for the input (no DOM needed). +function fakeInput(value = "", dataset = {}) { + return { value, dataset }; +} + +test("testing an unsaved typed URI preserves it (does not wipe the field)", async () => { + const controller = new ProfileWalletController({ + normalizeHexPubkey: (v) => (typeof v === "string" ? v : ""), + getActivePubkey: () => PUBKEY, + showSuccess: () => {}, + showStatus: () => {}, + showError: () => {}, + callbacks: { onWalletTest: () => {} }, + services: { + nwcSettings: { + // The wallet has NOT been saved yet — saved settings are empty. + getActiveNwcSettings: () => ({ nwcUri: "" }), + createDefaultNwcSettings: () => ({ nwcUri: "" }), + }, + }, + }); + + // Simulate the user having typed (unmasked) a URI into the field. + controller.walletUriInput = fakeInput(TYPED_URI, { secretValue: TYPED_URI, secretMasked: "false" }); + controller.walletDefaultZapInput = fakeInput("", {}); + + // Stub the network/test + busy state; let the real test() finally run. + controller.isWalletBusy = () => false; + controller.testWalletConnection = async () => ({ ok: true }); + controller.setWalletPaneBusy = () => {}; + controller.updateWalletStatus = () => {}; + controller.applyWalletControlState = () => {}; // not under test + + await controller.handleWalletTest(); + + assert.equal( + controller.getSecretInputValue(controller.walletUriInput), + TYPED_URI, + "the typed URI must survive a Test so the next Save persists it (not 'remove')", + ); +});