From 83ae4cc14db2937f399705a21c9abd5ca2d9ed31 Mon Sep 17 00:00:00 2001 From: sneak Date: Tue, 27 Oct 2026 10:34:54 +0000 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. The save writes only the fields that differ from what the page last saved. So when another page had stored a different URL, a "Save" of the URL this page loaded wrote nothing, and "Saved." showed over the other page's URL. A save refused because the network changed still stores nothing. 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..f9a0254 --- /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); + // The browser is not made to tell this page of the other page's write. + 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."); + }, +);