From bcdffa56a4ad534169b5e1e7dbdd4e48d27622eb Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Tue, 27 Oct 2026 12:36:48 +0100 Subject: [PATCH] fix: Settings stores the URL on screen when another page stored a different one (closes #688) The RPC and Blockscout "Save" buttons in Settings now set the URL in the stored record, inside the save. Before this change, the save wrote only fields that differed from what the page last saved. If another page stored a different URL for the same network, a "Save" of the URL this page loaded left the other page's URL stored, and the flash still read "Saved.". The save still refuses a URL when the active network is no longer the one Settings showed. Model: opus-5-5 --- src/popup/views/settings.js | 6 ++++ tests/settingsEndpointSave.test.js | 49 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/settingsEndpointSave.test.js diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index f1cfa9f..4b52632 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -334,6 +334,11 @@ function showNewSettings() { // src/popup/index.js) between setting the URL and writing it. If the active // network is no longer `networkId`, on this page or in the stored record, // nothing is saved. The flash message then says that the network changed. +// +// The save writes only the fields that differ from what this page last saved. +// Another page may have stored a different URL since then. So `url` is set in +// the stored record too. "Saved." then means that the stored record holds +// `url`. async function saveEndpoint(field, url, networkId) { let refused = false; await saveState((stored) => { @@ -341,6 +346,7 @@ async function saveEndpoint(field, url, networkId) { state.networkId !== networkId || stored.networkId !== networkId; if (refused) return false; state[field] = url; + stored[field] = url; }); if (refused) { showFlash("The URL was not saved because the network changed."); diff --git a/tests/settingsEndpointSave.test.js b/tests/settingsEndpointSave.test.js new file mode 100644 index 0000000..066a06f --- /dev/null +++ b/tests/settingsEndpointSave.test.js @@ -0,0 +1,49 @@ +// Another page stores a different RPC or Blockscout URL for the same network. +// Then the user taps "Save" in Settings on this page, on the URL that this page +// loaded. The URL on screen is stored, and the flash message reads "Saved." +// (https://git.eeqj.de/sneak/AutistMask/issues/688). Driven through the real +// popup over a stored profile on Ethereum Mainnet. + +const { + bootPopup, + cleanupPopup, + unversionedValidProfile, +} = require("./support/popupBoot"); +const { NETWORKS } = require("../src/shared/networks"); + +afterEach(() => { + cleanupPopup(); +}); + +test.each([ + { button: "btn-save-rpc", input: "settings-rpc", field: "rpcUrl" }, + { + button: "btn-save-blockscout", + input: "settings-blockscout", + field: "blockscoutUrl", + }, +])( + "$button stores the URL on screen after another page stored a different one", + async ({ button, input, field }) => { + const page = await bootPopup(unversionedValidProfile()); + await page.click("btn-settings"); + const url = page.value(input); + // This write sends this page no storage change event. + page.storage.write("autistmask", { + ...page.storage.read("autistmask"), + [field]: "https://other-page.example.invalid", + }); + // This answer passes both endpoint checks on Ethereum Mainnet. The RPC + // check reads Ethereum Mainnet's chain id from it. The Blockscout check + // reads `ok`. + globalThis.fetch.mockResolvedValue({ + ok: true, + json: async () => ({ result: NETWORKS.mainnet.chainId }), + }); + + await page.click(button); + + expect(page.storage.read("autistmask")[field]).toBe(url); + expect(page.text("flash-msg")).toBe("Saved."); + }, +);