tests/support/popupBoot.js recorded every interval the popup sets but left timeouts alone, so the two-second timer that clears a flash message could fire after cleanupPopup() had removed the page, throw "document is not defined", and fail whichever test file the jest worker had moved on to. Timeouts still run for real during a popup test; the helper now records each one until it fires or is cleared, and cleanupPopup() clears the ones still pending. The fake timers tests/firstWalletRefresh.test.js used to hold that timer are removed. tests/popupBoot.test.js shows a flash message, cleans up, waits past the flash duration, and fails if anything throws. Model: opus-5-5
136 lines
4.7 KiB
JavaScript
136 lines
4.7 KiB
JavaScript
// 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");
|
|
}
|
|
|
|
beforeEach(() => {
|
|
chainBalance = "0.0";
|
|
});
|
|
|
|
afterEach(() => {
|
|
cleanupPopup();
|
|
});
|
|
|
|
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);
|
|
});
|
|
});
|