diff --git a/README.md b/README.md index dfd0dd6..2f6a7c8 100644 --- a/README.md +++ b/README.md @@ -471,7 +471,9 @@ When "Back" returns to a screen that this page load has not drawn yet, Home is shown instead. "Back" onto a screen already drawn only unhides it, so unsaved input in Settings survives. Home is drawn again on every "Back". When a balance refresh returns while AddressDetail or AddressToken is showing, that -screen draws its balances again. +screen draws its balances again. When a refresh returns while Send is showing, +Send draws its From address and current balance again. Its inputs and dropdown +stay as the user left them. A network switch, or a change to "Use Blockscout", cuts the stack at the first screen that cannot be drawn from saved state. The cut also drops AddressToken diff --git a/src/popup/index.js b/src/popup/index.js index f9c8dd9..513c69e 100644 --- a/src/popup/index.js +++ b/src/popup/index.js @@ -107,8 +107,10 @@ let refreshTimer = null; // tracked token list stays as it is. // // Once the save is done, the wallet list is drawn again. So are the balances -// on AddressDetail or AddressToken when that screen is up. No other screen is -// drawn again, DeleteAddress and its balance lines included. +// on AddressDetail or AddressToken when that screen is up. When Send is up, its +// From address and current balance are drawn again, and its inputs and dropdown +// stay as the user left them. No other screen is drawn again, DeleteAddress and +// its balance lines included. async function doRefreshAndRender() { if (refreshInFlight) { refreshAgain = true; @@ -164,6 +166,7 @@ async function doRefreshAndRender() { renderWalletList(); if (state.currentView === "address") addressDetail.renderBalances(); if (state.currentView === "address-token") addressToken.renderBalance(); + if (state.currentView === "send") send.updateSendBalance(); } catch (e) { // No caller waits for this function, so it must never reject. An // unhandled rejection reports nothing. The save inside it reports its diff --git a/tests/addedTokenBalance.test.js b/tests/addedTokenBalance.test.js index 84f4f79..18b1e80 100644 --- a/tests/addedTokenBalance.test.js +++ b/tests/addedTokenBalance.test.js @@ -19,6 +19,10 @@ // its ETH line reads the same, and Send states it as unknown // (https://git.eeqj.de/sneak/AutistMask/issues/736). // +// A refresh that returns while Send is up draws Send's balance line again, and +// leaves what the user entered as it is +// (https://git.eeqj.de/sneak/AutistMask/issues/748). +// // Driven through the real popup over a stored profile on Ethereum Mainnet, // with "Show tracked tokens with zero balance" on, as it is by default. @@ -431,6 +435,59 @@ test("Send for the new token reads its balance as unknown before any refresh has await finish([MILLION]); }); +test("Send for the new token reads the fetched balance once the refresh returns, without being reopened", async () => { + const page = await bootOnAddress(); + const finish = holdNextRefresh(page); + await addNewToken(page); + await openToken(page, NEW_TOKEN.toLowerCase()); + await page.click("btn-address-token-send"); + expect(page.text("send-balance")).toBe("Current balance: unknown (NEW)"); + + await finish([MILLION]); + + expect(page.visibleViews()).toEqual(["send"]); + expect(page.text("send-balance")).toBe("Current balance: 1000000.0000 NEW"); +}); + +test("a refresh that returns while Send is up draws an unread ETH balance again and leaves the inputs alone", async () => { + const page = await bootPopup(profileNotRead()); + // The dropdown's first option, which a browser selects by itself. + page.node("send-token").value = "ETH"; + await page.click("btn-main-send"); + expect(page.text("send-balance")).toBe("Current balance: unknown (ETH)"); + const recipient = "0x" + "dd".repeat(20); + page.node("send-to").value = recipient; + page.node("send-amount").value = "0.5"; + // Stands for the options on the screen, which only opening Send replaces. + page.node("send-token").innerHTML = "options on the screen"; + + await refreshOnce(page, []); + + expect(page.visibleViews()).toEqual(["send"]); + expect(page.text("send-balance")).toBe("Current balance: 2.0000 ETH"); + expect(page.value("send-to")).toBe(recipient); + expect(page.value("send-amount")).toBe("0.5"); + expect(page.value("send-token")).toBe("ETH"); + expect(page.node("send-token").innerHTML).toBe("options on the screen"); +}); + +test("a refresh that returns while Send is up keeps the token picked in the dropdown and draws its new balance", async () => { + const page = await bootOnDerivingWallet(); + const dropdown = page.node("send-token"); + dropdown.value = "ETH"; + await page.click("btn-main-send"); + dropdown.value = NEW_TOKEN; + for (const fn of dropdown.listeners.change) await fn(); + expect(page.text("send-balance")).toBe("Current balance: 1000000.0000 NEW"); + page.node("send-amount").value = "0.5"; + + await refreshOnce(page, [{ ...MILLION, balance: "3000000.0" }]); + + expect(page.text("send-balance")).toBe("Current balance: 3000000.0000 NEW"); + expect(page.value("send-token")).toBe(NEW_TOKEN); + expect(page.value("send-amount")).toBe("0.5"); +}); + test("a token added by contract address on SettingsAddToken reads quantity unknown until the refresh returns", async () => { const page = await bootPopup(unversionedValidProfile()); expect(page.visibleViews()).toEqual(["main"]);