Compare commits
1
Commits
next
...
b6bce56b07
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6bce56b07 |
+17
-4
@@ -64,8 +64,8 @@ window.addEventListener("pagehide", () => pageClosed.abort());
|
||||
|
||||
let refreshInFlight = false;
|
||||
|
||||
// The ten-second refresh init() starts, stopped when the popup moves to the
|
||||
// recovery screen: there is no profile left to refresh.
|
||||
// The ten-second refresh startRefresh() starts, stopped when the popup moves to
|
||||
// the recovery screen: there is no profile left to refresh.
|
||||
let refreshTimer = null;
|
||||
|
||||
async function doRefreshAndRender() {
|
||||
@@ -99,9 +99,23 @@ async function doRefreshAndRender() {
|
||||
}
|
||||
}
|
||||
|
||||
// Refreshes now and every ten seconds from then on. Called once a wallet
|
||||
// exists: by init() when the popup opens with one, and by AddWallet when it
|
||||
// adds one, which in a popup that opened on Welcome is the first
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/530). With the refresh already
|
||||
// running it starts no second one, and on the recovery screen it does nothing.
|
||||
function startRefresh() {
|
||||
if (!$("view-state-recovery").classList.contains("hidden")) return;
|
||||
doRefreshAndRender();
|
||||
if (refreshTimer === null) {
|
||||
refreshTimer = setInterval(doRefreshAndRender, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
const ctx = {
|
||||
renderWalletList,
|
||||
doRefreshAndRender,
|
||||
startRefresh,
|
||||
pageClosed: pageClosed.signal,
|
||||
showAddWalletView: () => {
|
||||
pushCurrentView();
|
||||
@@ -318,8 +332,7 @@ async function init() {
|
||||
} else {
|
||||
renderWalletList();
|
||||
restoreView();
|
||||
doRefreshAndRender();
|
||||
refreshTimer = setInterval(doRefreshAndRender, 10000);
|
||||
startRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ async function importMnemonic(ctx) {
|
||||
showFlash("Ready.", 1000);
|
||||
}
|
||||
|
||||
ctx.doRefreshAndRender();
|
||||
ctx.startRefresh();
|
||||
}
|
||||
|
||||
async function importPrivateKey(ctx) {
|
||||
@@ -274,7 +274,7 @@ async function importPrivateKey(ctx) {
|
||||
ctx.renderWalletList();
|
||||
showView("main");
|
||||
|
||||
ctx.doRefreshAndRender();
|
||||
ctx.startRefresh();
|
||||
}
|
||||
|
||||
async function importXprvKey(ctx) {
|
||||
@@ -368,7 +368,7 @@ async function importXprvKey(ctx) {
|
||||
showFlash("Ready.", 1000);
|
||||
}
|
||||
|
||||
ctx.doRefreshAndRender();
|
||||
ctx.startRefresh();
|
||||
}
|
||||
|
||||
function init(ctx) {
|
||||
|
||||
@@ -230,7 +230,7 @@ beforeEach(() => {
|
||||
|
||||
addWallet.init({
|
||||
renderWalletList: () => {},
|
||||
doRefreshAndRender: () => {},
|
||||
startRefresh: () => {},
|
||||
pageClosed: new AbortController().signal,
|
||||
});
|
||||
confirmTx.init({});
|
||||
|
||||
@@ -94,7 +94,7 @@ describe.each([
|
||||
async function create(pageClosed) {
|
||||
addWallet.init({
|
||||
renderWalletList: () => {},
|
||||
doRefreshAndRender: () => {},
|
||||
startRefresh: () => {},
|
||||
pageClosed,
|
||||
});
|
||||
element("tab-" + mode).listeners.click();
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
// A popup that opened with no wallet never started the ten-second refresh, so
|
||||
// after the first wallet was added Home went on showing the balance it had then
|
||||
// until the popup was reopened
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/530). Each test boots the real
|
||||
// popup, adds wallets through AddWallet, and runs the intervals the popup set
|
||||
// with tick(). The balance stand-in writes what the chain holds onto every
|
||||
// address, as the real refreshBalances() does.
|
||||
|
||||
const { HDNodeWallet, Mnemonic } = require("ethers");
|
||||
|
||||
const {
|
||||
bootPopup,
|
||||
cleanupPopup,
|
||||
unversionedValidProfile,
|
||||
ADDRESS,
|
||||
} = require("./support/popupBoot");
|
||||
|
||||
const PHRASE =
|
||||
"abandon abandon abandon abandon abandon abandon " +
|
||||
"abandon abandon abandon abandon abandon about";
|
||||
const PRIVATE_KEY = "0x" + "11".repeat(32);
|
||||
const XPRV = HDNodeWallet.fromSeed(
|
||||
Mnemonic.fromPhrase(PHRASE).computeSeed(),
|
||||
).extendedKey;
|
||||
const PASSWORD = "correct horse battery staple";
|
||||
|
||||
const TABS = [
|
||||
{ tab: "tab-mnemonic", field: "wallet-mnemonic", secret: PHRASE },
|
||||
{ tab: "tab-privkey", field: "import-private-key", secret: PRIVATE_KEY },
|
||||
{ tab: "tab-xprv", field: "import-xprv-key", secret: XPRV },
|
||||
];
|
||||
|
||||
// A record this build cannot read: its wallets are a string.
|
||||
const UNREADABLE = {
|
||||
hasWallet: true,
|
||||
wallets: ADDRESS,
|
||||
activeAddress: ADDRESS,
|
||||
};
|
||||
|
||||
// The ETH balance the chain holds for every address, in ether.
|
||||
let chainBalance = "0.0";
|
||||
|
||||
// Boots the popup over `stored`, with the balance stand-in reading the chain.
|
||||
async function boot(stored) {
|
||||
const page = await bootPopup(stored);
|
||||
const { refreshBalances } = require("../src/shared/balances");
|
||||
refreshBalances.mockImplementation(async (wallets) => {
|
||||
for (const wallet of wallets) {
|
||||
for (const addr of wallet.addresses) addr.balance = chainBalance;
|
||||
}
|
||||
});
|
||||
return { page, refreshBalances };
|
||||
}
|
||||
|
||||
// Fills in the open AddWallet screen and presses "Import".
|
||||
async function importOn(page, { tab, field, secret }) {
|
||||
await page.click(tab);
|
||||
page.node(field).value = secret;
|
||||
page.node("add-wallet-password").value = PASSWORD;
|
||||
page.node("add-wallet-password-confirm").value = PASSWORD;
|
||||
await page.click("btn-add-wallet-confirm");
|
||||
}
|
||||
|
||||
// Fake timers hold the timer of the flash message an import ends with, which
|
||||
// would otherwise fire after cleanupPopup() has removed the page and throw.
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
chainBalance = "0.0";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupPopup();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
describe("a popup that opened on Welcome", () => {
|
||||
test.each(TABS)(
|
||||
"after the first wallet is added from $tab, funds that arrive show on Home",
|
||||
async (entry) => {
|
||||
const { page } = await boot(undefined);
|
||||
expect(page.pageErrors).toEqual([]);
|
||||
await page.click("btn-welcome-add");
|
||||
await importOn(page, entry);
|
||||
expect(page.visibleViews()).toEqual(["main"]);
|
||||
expect(page.text("total-value")).toBe("0.0000 ETH");
|
||||
|
||||
chainBalance = "2.0";
|
||||
await page.tick();
|
||||
|
||||
expect(page.text("total-value")).toBe("2.0000 ETH");
|
||||
},
|
||||
);
|
||||
|
||||
test("a second wallet starts no second refresh", async () => {
|
||||
const { page, refreshBalances } = await boot(undefined);
|
||||
await page.click("btn-welcome-add");
|
||||
await importOn(page, TABS[1]);
|
||||
await page.click("btn-add-wallet-bottom");
|
||||
await importOn(page, TABS[0]);
|
||||
const calls = refreshBalances.mock.calls.length;
|
||||
|
||||
await page.tick();
|
||||
|
||||
expect(refreshBalances).toHaveBeenCalledTimes(calls + 1);
|
||||
});
|
||||
|
||||
test("a wallet added as the recovery screen goes up starts no refresh", async () => {
|
||||
const { page, refreshBalances } = await boot(undefined);
|
||||
// The stored record becomes unreadable during the address scan that
|
||||
// follows the import, and a save finds it.
|
||||
const { scanForAddresses } = require("../src/shared/balances");
|
||||
const { saveState } = require("../src/shared/state");
|
||||
scanForAddresses.mockImplementationOnce(async () => {
|
||||
page.storage.write("autistmask", UNREADABLE);
|
||||
await saveState().catch(() => {});
|
||||
return { addresses: [], nextIndex: 1 };
|
||||
});
|
||||
await page.click("btn-welcome-add");
|
||||
await importOn(page, TABS[0]);
|
||||
expect(page.visibleViews()).toEqual(["state-recovery"]);
|
||||
|
||||
await page.tick();
|
||||
|
||||
expect(refreshBalances).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("a popup that opened with a wallet", () => {
|
||||
test("adding a wallet starts no second refresh", async () => {
|
||||
const { page, refreshBalances } = await boot(unversionedValidProfile());
|
||||
await page.click("btn-add-wallet-bottom");
|
||||
await importOn(page, TABS[1]);
|
||||
const calls = refreshBalances.mock.calls.length;
|
||||
|
||||
await page.tick();
|
||||
|
||||
expect(refreshBalances).toHaveBeenCalledTimes(calls + 1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user