fix: Home's Send and Receive clear the selected token (closes #753) #754
@@ -581,8 +581,9 @@ Transitions:
|
|||||||
- "+": derives the next address in place.
|
- "+": derives the next address in place.
|
||||||
- `[info]`: AddressDetail.
|
- `[info]`: AddressDetail.
|
||||||
- `[x]`: DeleteAddress.
|
- `[x]`: DeleteAddress.
|
||||||
- "Send": Send. Refused with a flash message when the active address has a zero
|
- "Send": Send, with its dropdown on ETH. Refused with a flash message when the
|
||||||
ETH balance or belongs to a wallet carrying the `walletDefects.js` notice.
|
active address has a zero ETH balance or belongs to a wallet carrying the
|
||||||
|
`walletDefects.js` notice.
|
||||||
- "Receive": Receive.
|
- "Receive": Receive.
|
||||||
- Tap a transaction row: TransactionDetail.
|
- Tap a transaction row: TransactionDetail.
|
||||||
- "Add additional wallet...": AddWallet.
|
- "Add additional wallet...": AddWallet.
|
||||||
|
|||||||
@@ -510,11 +510,15 @@ function init(ctx) {
|
|||||||
showFlash("Cannot send \u2014 zero balance.");
|
showFlash("Cannot send \u2014 zero balance.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Send uses a selected token ahead of its dropdown, and AddressToken
|
||||||
|
// can leave one selected on the way here.
|
||||||
|
state.selectedToken = null;
|
||||||
$("send-to").value = "";
|
$("send-to").value = "";
|
||||||
$("send-amount").value = "";
|
$("send-amount").value = "";
|
||||||
$("send-token-dropdown").classList.remove("hidden");
|
$("send-token-dropdown").classList.remove("hidden");
|
||||||
$("send-token-static").classList.add("hidden");
|
$("send-token-static").classList.add("hidden");
|
||||||
renderSendTokenSelect(addr);
|
renderSendTokenSelect(addr);
|
||||||
|
$("send-token").value = "ETH";
|
||||||
updateSendBalance();
|
updateSendBalance();
|
||||||
resetSendValidation();
|
resetSendValidation();
|
||||||
pushCurrentView();
|
pushCurrentView();
|
||||||
@@ -526,6 +530,9 @@ function init(ctx) {
|
|||||||
showFlash("No active address selected.");
|
showFlash("No active address selected.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Receive warns about a selected token, and AddressToken can leave one
|
||||||
|
// selected on the way here.
|
||||||
|
state.selectedToken = null;
|
||||||
ctx.showReceive();
|
ctx.showReceive();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
// Home's "Send" and "Receive" open with no token selected. Three routes reach
|
||||||
|
// Home with a token still selected, Send reads that token ahead of its dropdown,
|
||||||
|
// and Receive warns about it
|
||||||
|
// (https://git.eeqj.de/sneak/AutistMask/issues/753). Driven through the real
|
||||||
|
// popup over a stored profile.
|
||||||
|
|
||||||
|
const {
|
||||||
|
bootPopup,
|
||||||
|
cleanupPopup,
|
||||||
|
unversionedValidProfile,
|
||||||
|
TOKEN_ADDRESS,
|
||||||
|
} = require("./support/popupBoot");
|
||||||
|
|
||||||
|
const RECIPIENT = "0x711e691962E5c054b78071C789A0C8CE657377aD";
|
||||||
|
const OTHER_WALLET_ADDRESS = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
|
||||||
|
const PRIVATE_KEY = "0x" + "11".repeat(32);
|
||||||
|
const PASSWORD = "correct horse battery staple";
|
||||||
|
const TOKEN = {
|
||||||
|
address: TOKEN_ADDRESS,
|
||||||
|
symbol: "TST",
|
||||||
|
decimals: 18,
|
||||||
|
name: "Test Token",
|
||||||
|
};
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupPopup();
|
||||||
|
});
|
||||||
|
|
||||||
|
// AddressToken opened from AddressDetail, as tapping the token's row does.
|
||||||
|
async function openToken(page) {
|
||||||
|
const { state } = require("../src/shared/state");
|
||||||
|
state.selectedToken = TOKEN_ADDRESS;
|
||||||
|
require("../src/popup/views/helpers").pushCurrentView();
|
||||||
|
require("../src/popup/views/addressToken").show();
|
||||||
|
await page.settle();
|
||||||
|
expect(page.visibleViews()).toEqual(["address-token"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The popup open on AddressToken, at an address holding 1.5 ETH and 2.5 TST.
|
||||||
|
// A second wallet is there for the route that deletes one.
|
||||||
|
async function bootOnToken() {
|
||||||
|
const profile = unversionedValidProfile({
|
||||||
|
currentView: "address",
|
||||||
|
viewStack: ["main"],
|
||||||
|
selectedWallet: 0,
|
||||||
|
selectedAddress: 0,
|
||||||
|
trackedTokens: [TOKEN],
|
||||||
|
});
|
||||||
|
profile.wallets[0].addresses[0].tokenBalances = [
|
||||||
|
{ ...TOKEN, balance: "2.5" },
|
||||||
|
];
|
||||||
|
profile.wallets.push({
|
||||||
|
type: "hd",
|
||||||
|
name: "Wallet 2",
|
||||||
|
xpub: "xpub-wallet-2",
|
||||||
|
encryptedSecret: "encrypted-secret-2",
|
||||||
|
nextIndex: 1,
|
||||||
|
addresses: [
|
||||||
|
{
|
||||||
|
address: OTHER_WALLET_ADDRESS,
|
||||||
|
balance: "0",
|
||||||
|
tokenBalances: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const page = await bootPopup(profile);
|
||||||
|
await openToken(page);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reopenAfterTokenSend() {
|
||||||
|
const closed = await bootOnToken();
|
||||||
|
await closed.click("btn-address-token-send");
|
||||||
|
cleanupPopup();
|
||||||
|
return bootPopup(undefined, { storage: closed.storage });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addWalletFromTokenSettings() {
|
||||||
|
const page = await bootOnToken();
|
||||||
|
await page.click("btn-settings");
|
||||||
|
await page.click("btn-main-add-wallet");
|
||||||
|
await page.click("tab-privkey");
|
||||||
|
page.node("import-private-key").value = PRIVATE_KEY;
|
||||||
|
page.node("add-wallet-password").value = PASSWORD;
|
||||||
|
page.node("add-wallet-password-confirm").value = PASSWORD;
|
||||||
|
await page.click("btn-add-wallet-confirm");
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes through the lost-password screen. It asks for the wallet's name, and
|
||||||
|
// the stored wallets here have no real password.
|
||||||
|
async function deleteWalletFromTokenSettings() {
|
||||||
|
const page = await bootOnToken();
|
||||||
|
await page.click("btn-settings");
|
||||||
|
// What the second wallet's [x] button in Settings does.
|
||||||
|
require("../src/popup/views/helpers").pushCurrentView();
|
||||||
|
require("../src/popup/views/deleteWallet").show(1);
|
||||||
|
await page.click("btn-delete-wallet-lost-password");
|
||||||
|
page.node("delete-wallet-lost-name-input").value = "Wallet 2";
|
||||||
|
await page.click("btn-delete-wallet-lost-confirm");
|
||||||
|
await page.click("btn-settings-back");
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ROUTES = [
|
||||||
|
["reopening the popup after AddressToken's Send", reopenAfterTokenSend],
|
||||||
|
[
|
||||||
|
"the Settings gear on AddressToken, then adding a wallet",
|
||||||
|
addWalletFromTokenSettings,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"the Settings gear on AddressToken, then deleting another wallet, then Back",
|
||||||
|
deleteWalletFromTokenSettings,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// A route that stopped reaching Home with the token selected would prove nothing
|
||||||
|
// in the tests below, so it fails here.
|
||||||
|
async function arriveOnHome(route) {
|
||||||
|
const page = await route();
|
||||||
|
const { state } = require("../src/shared/state");
|
||||||
|
expect(page.visibleViews()).toEqual(["main"]);
|
||||||
|
expect(state.selectedToken).toBe(TOKEN_ADDRESS);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ETH chosen in Send's dropdown, as the user does.
|
||||||
|
async function chooseEth(page) {
|
||||||
|
const dropdown = page.node("send-token");
|
||||||
|
dropdown.value = "ETH";
|
||||||
|
for (const fn of dropdown.listeners.change) await fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Review for 1 of the token Send has, to RECIPIENT.
|
||||||
|
async function review(page) {
|
||||||
|
page.node("send-to").value = RECIPIENT;
|
||||||
|
page.node("send-amount").value = "1";
|
||||||
|
await page.click("btn-send-review");
|
||||||
|
expect(page.visibleViews()).toEqual(["confirm-tx"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe.each(ROUTES)("after %s", (_name, route) => {
|
||||||
|
test("choosing ETH on Home's Send reviews ETH", async () => {
|
||||||
|
const page = await arriveOnHome(route);
|
||||||
|
|
||||||
|
await page.click("btn-main-send");
|
||||||
|
await chooseEth(page);
|
||||||
|
await review(page);
|
||||||
|
|
||||||
|
expect(page.text("confirm-type")).toBe("Native ETH transfer");
|
||||||
|
expect(page.storage.read("autistmask").selectedToken).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Home's Send starts on ETH", async () => {
|
||||||
|
const page = await arriveOnHome(route);
|
||||||
|
|
||||||
|
await page.click("btn-main-send");
|
||||||
|
|
||||||
|
expect(page.text("send-balance")).toBe("Current balance: 1.5000 ETH");
|
||||||
|
expect(page.value("send-token")).toBe("ETH");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Home's Receive shows no token warning", async () => {
|
||||||
|
const page = await arriveOnHome(route);
|
||||||
|
|
||||||
|
await page.click("btn-main-receive");
|
||||||
|
|
||||||
|
expect(page.visibleViews()).toEqual(["receive"]);
|
||||||
|
expect(page.text("receive-erc20-warning")).toBe("");
|
||||||
|
expect(page.storage.read("autistmask").selectedToken).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("AddressToken's Send still sends the token after Home's Send has opened", async () => {
|
||||||
|
const page = await arriveOnHome(deleteWalletFromTokenSettings);
|
||||||
|
await page.click("btn-main-send");
|
||||||
|
await page.click("btn-send-back");
|
||||||
|
await openToken(page);
|
||||||
|
|
||||||
|
await page.click("btn-address-token-send");
|
||||||
|
|
||||||
|
expect(page.text("send-balance")).toBe("Current balance: 2.5000 TST");
|
||||||
|
await review(page);
|
||||||
|
expect(page.text("confirm-type")).toBe("ERC-20 token transfer (TST)");
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user