fix: a second click on Add while the lookup waits tracks the token once (closes #727) #728
@@ -17,6 +17,11 @@ const { log } = require("../../shared/log");
|
|||||||
// AddToken was last opened changes neither the screen nor the status line.
|
// AddToken was last opened changes neither the screen nor the status line.
|
||||||
let addTokenScreenOpenings = 0;
|
let addTokenScreenOpenings = 0;
|
||||||
|
|
||||||
|
function findTracked(address) {
|
||||||
|
const lower = address.toLowerCase();
|
||||||
|
return state.trackedTokens.find((t) => t.address.toLowerCase() === lower);
|
||||||
|
}
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
addTokenScreenOpenings++;
|
addTokenScreenOpenings++;
|
||||||
$("add-token-address").value = "";
|
$("add-token-address").value = "";
|
||||||
@@ -50,9 +55,7 @@ function init(ctx) {
|
|||||||
showFlash("Enter a valid contract address starting with 0x.");
|
showFlash("Enter a valid contract address starting with 0x.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const already = state.trackedTokens.find(
|
const already = findTracked(contractAddr);
|
||||||
(t) => t.address.toLowerCase() === contractAddr.toLowerCase(),
|
|
||||||
);
|
|
||||||
if (already) {
|
if (already) {
|
||||||
showFlash(already.symbol + " is already being tracked.");
|
showFlash(already.symbol + " is already being tracked.");
|
||||||
return;
|
return;
|
||||||
@@ -85,6 +88,18 @@ function init(ctx) {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Another click on Add may have tracked this token while this
|
||||||
|
// lookup waited.
|
||||||
|
const trackedMeanwhile = findTracked(contractAddr);
|
||||||
|
if (trackedMeanwhile) {
|
||||||
|
showFlash(
|
||||||
|
trackedMeanwhile.symbol + " is already being tracked.",
|
||||||
|
);
|
||||||
|
if (addTokenScreenOpenings !== opening) return;
|
||||||
|
infoEl.textContent = "";
|
||||||
|
infoEl.style.visibility = "hidden";
|
||||||
|
return;
|
||||||
|
}
|
||||||
// The balance list never shows a token that the spoof rule
|
// The balance list never shows a token that the spoof rule
|
||||||
// refuses, so such a token is not tracked.
|
// refuses, so such a token is not tracked.
|
||||||
if (isSpoofedSymbol(info.symbol, contractAddr, chainId)) {
|
if (isSpoofedSymbol(info.symbol, contractAddr, chainId)) {
|
||||||
|
|||||||
@@ -163,6 +163,14 @@ function init(_ctx) {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Another click on Add, or a "Top tokens:" button, may have
|
||||||
|
// tracked this token while this lookup waited.
|
||||||
|
if (isTracked(addr)) {
|
||||||
|
showFlash("Already tracked.");
|
||||||
|
infoEl.textContent = "";
|
||||||
|
infoEl.style.visibility = "hidden";
|
||||||
|
return;
|
||||||
|
}
|
||||||
// The balance list never shows a token that the spoof rule
|
// The balance list never shows a token that the spoof rule
|
||||||
// refuses, so such a token is not tracked. The rule never refuses
|
// refuses, so such a token is not tracked. The rule never refuses
|
||||||
// a bundled token, so the "Top tokens:" buttons and the top 100
|
// a bundled token, so the "Top tokens:" buttons and the top 100
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
// A contract lookup on AddToken can answer after the user has left AddToken.
|
// A contract lookup on AddToken can answer after the user has left AddToken.
|
||||||
// The token is still added, and the screen the user is on does not change
|
// The token is still added, and the screen the user is on does not change
|
||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/603). An answer while AddToken
|
// (https://git.eeqj.de/sneak/AutistMask/issues/603). An answer while AddToken
|
||||||
// still shows goes to AddressDetail. Driven through the real popup over a
|
// still shows goes to AddressDetail. On either add-token screen, an answer for a
|
||||||
// stored profile.
|
// token tracked while the lookup waited tracks nothing
|
||||||
|
// (https://git.eeqj.de/sneak/AutistMask/issues/727). Driven through the real
|
||||||
|
// popup over a stored profile.
|
||||||
|
|
||||||
const {
|
const {
|
||||||
bootPopup,
|
bootPopup,
|
||||||
@@ -43,9 +45,9 @@ async function openAddToken() {
|
|||||||
return boot;
|
return boot;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Presses Add with a contract lookup that answers only when the function
|
// Presses `button` with a contract lookup that answers only when the function
|
||||||
// returned here is called. That function waits for Add to finish.
|
// returned here is called. That function waits for the add to finish.
|
||||||
function addWithPendingLookup(boot) {
|
function addWithPendingLookup(boot, button = "btn-add-token-confirm") {
|
||||||
const { lookupTokenInfo } = require("../src/shared/balances");
|
const { lookupTokenInfo } = require("../src/shared/balances");
|
||||||
let answer;
|
let answer;
|
||||||
lookupTokenInfo.mockImplementationOnce(
|
lookupTokenInfo.mockImplementationOnce(
|
||||||
@@ -54,7 +56,7 @@ function addWithPendingLookup(boot) {
|
|||||||
answer = resolve;
|
answer = resolve;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const adding = boot.click("btn-add-token-confirm");
|
const adding = boot.click(button);
|
||||||
return async () => {
|
return async () => {
|
||||||
answer({
|
answer({
|
||||||
symbol: TOKEN.symbol,
|
symbol: TOKEN.symbol,
|
||||||
@@ -136,3 +138,78 @@ test("an answer while AddToken still shows goes to AddressDetail with the token
|
|||||||
expect(boot.node("address-balances").innerHTML).toContain("TST");
|
expect(boot.node("address-balances").innerHTML).toContain("TST");
|
||||||
expect(boot.storage.read("autistmask").trackedTokens).toEqual([TOKEN]);
|
expect(boot.storage.read("autistmask").trackedTokens).toEqual([TOKEN]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("an answer for a token tracked after AddToken was opened again leaves the status line as it is", async () => {
|
||||||
|
const boot = await openAddToken();
|
||||||
|
const answerFirstLookup = addWithPendingLookup(boot);
|
||||||
|
await boot.click("btn-add-token-back");
|
||||||
|
await boot.click("btn-add-token");
|
||||||
|
boot.node("add-token-address").value = OTHER_TOKEN_ADDRESS;
|
||||||
|
// The second lookup is never answered.
|
||||||
|
addWithPendingLookup(boot);
|
||||||
|
require("../src/popup/views/helpers").trackToken(TOKEN);
|
||||||
|
|
||||||
|
await answerFirstLookup();
|
||||||
|
expect(boot.text("flash-msg")).toBe("TST is already being tracked.");
|
||||||
|
expect(boot.text("add-token-info")).toBe("Looking up token...");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Each screen is opened from AddressDetail by the buttons named, in order.
|
||||||
|
describe.each([
|
||||||
|
[
|
||||||
|
"AddToken",
|
||||||
|
["btn-add-token"],
|
||||||
|
"add-token-address",
|
||||||
|
"btn-add-token-confirm",
|
||||||
|
"add-token-info",
|
||||||
|
"TST is already being tracked.",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"SettingsAddToken",
|
||||||
|
["btn-settings", "btn-settings-add-token"],
|
||||||
|
"settings-addtoken-address",
|
||||||
|
"btn-settings-addtoken-manual",
|
||||||
|
"settings-addtoken-info",
|
||||||
|
"Already tracked.",
|
||||||
|
],
|
||||||
|
])("%s", (_, opening, field, button, statusLine, alreadyTrackedFlash) => {
|
||||||
|
// The screen open with TOKEN's contract address typed in.
|
||||||
|
async function openScreen() {
|
||||||
|
const boot = await bootPopup(
|
||||||
|
unversionedValidProfile({
|
||||||
|
currentView: "address",
|
||||||
|
selectedWallet: 0,
|
||||||
|
selectedAddress: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(boot.pageErrors).toEqual([]);
|
||||||
|
for (const id of opening) await boot.click(id);
|
||||||
|
boot.node(field).value = TOKEN.address;
|
||||||
|
return boot;
|
||||||
|
}
|
||||||
|
|
||||||
|
test("a second click on Add while the lookup waits tracks the token once", async () => {
|
||||||
|
const boot = await openScreen();
|
||||||
|
const answerFirstClick = addWithPendingLookup(boot, button);
|
||||||
|
const answerSecondClick = addWithPendingLookup(boot, button);
|
||||||
|
|
||||||
|
await answerFirstClick();
|
||||||
|
await answerSecondClick();
|
||||||
|
expect(boot.text("flash-msg")).toBe(alreadyTrackedFlash);
|
||||||
|
expect(boot.storage.read("autistmask").trackedTokens).toEqual([TOKEN]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("an answer for a token tracked while the lookup waited clears the status line", async () => {
|
||||||
|
const boot = await openScreen();
|
||||||
|
const answerLookup = addWithPendingLookup(boot, button);
|
||||||
|
expect(boot.text(statusLine)).toBe("Looking up token...");
|
||||||
|
require("../src/popup/views/helpers").trackToken(TOKEN);
|
||||||
|
|
||||||
|
await answerLookup();
|
||||||
|
expect(boot.text("flash-msg")).toBe(alreadyTrackedFlash);
|
||||||
|
expect(boot.text(statusLine)).toBe("");
|
||||||
|
expect(boot.node(statusLine).style.visibility).toBe("hidden");
|
||||||
|
const { state } = require("../src/shared/state");
|
||||||
|
expect(state.trackedTokens).toEqual([TOKEN]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user