The transaction lists and ENS name lookups on the address and token screens, the address scan after a wallet is created, the endpoint checks in Settings, the wait screen's receipt check, the Send screen's Max fee estimate and the token lookup on both add-token screens now check the signal the popup aborts on pagehide before reporting a failed request. scanForAddresses(), resolveEnsNames() and lookupTokenInfo() take the signal. End-to-end tests reload the popup on the address screen and during the address scan with their requests held. Jest tests show each of these reports a real failure and stays silent once the popup has closed. The transaction detail and confirmation screens are left out: they discard the popup context that carries the signal. Model: opus-5-5
96 lines
3.1 KiB
JavaScript
96 lines
3.1 KiB
JavaScript
// The balance refresh, and the address scan after a wallet is created, do not
|
|
// report a request the popup's own closing cancelled, and still report one
|
|
// that failed while the popup was open
|
|
// (https://git.eeqj.de/sneak/AutistMask/issues/218,
|
|
// https://git.eeqj.de/sneak/AutistMask/issues/475).
|
|
//
|
|
// In the popup a cancelled fetch() fails with the same "Failed to fetch" as a
|
|
// server that cannot be reached, so every request here fails that way, and
|
|
// only the signal the popup aborts on pagehide tells the two cases apart.
|
|
|
|
const { FetchRequest } = require("ethers");
|
|
const { refreshBalances, scanForAddresses } = require("../src/shared/balances");
|
|
const {
|
|
generateMnemonic,
|
|
hdWalletFromMnemonic,
|
|
} = require("../src/shared/wallet");
|
|
|
|
const RPC_URL = "https://rpc.example.invalid";
|
|
const EXPLORER_URL = "https://explorer.example.invalid/api/v2";
|
|
const ADDRESS = "0x1111111111111111111111111111111111111111";
|
|
|
|
const realFetch = globalThis.fetch;
|
|
let logged;
|
|
|
|
beforeEach(() => {
|
|
logged = [];
|
|
jest.spyOn(console, "error").mockImplementation((...args) => {
|
|
logged.push(args.map(String).join(" "));
|
|
});
|
|
const failedToFetch = async () => {
|
|
throw new TypeError("Failed to fetch");
|
|
};
|
|
// The RPC calls (ETH balance, ENS name, and the scan's balance and
|
|
// transaction count) and the explorer request (token balances) all fail
|
|
// the same way.
|
|
FetchRequest.registerGetUrl(failedToFetch);
|
|
globalThis.fetch = jest.fn(failedToFetch);
|
|
});
|
|
|
|
afterEach(() => {
|
|
FetchRequest.registerGetUrl(FetchRequest.createGetUrlFunc());
|
|
globalThis.fetch = realFetch;
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
function refresh(signal) {
|
|
const wallets = [{ addresses: [{ address: ADDRESS }] }];
|
|
return refreshBalances(
|
|
wallets,
|
|
RPC_URL,
|
|
EXPLORER_URL,
|
|
[],
|
|
"mainnet",
|
|
signal,
|
|
);
|
|
}
|
|
|
|
test("a failure while the popup is open is reported", async () => {
|
|
await refresh(new AbortController().signal);
|
|
for (const label of [
|
|
"ETH balance failed",
|
|
"ENS reverse failed",
|
|
"fetchTokenBalances failed: Failed to fetch",
|
|
]) {
|
|
expect(logged.some((line) => line.includes(label))).toBe(true);
|
|
}
|
|
});
|
|
|
|
test("a failure once the popup has closed is not", async () => {
|
|
const pageClosed = new AbortController();
|
|
pageClosed.abort();
|
|
await refresh(pageClosed.signal);
|
|
expect(logged).toEqual([]);
|
|
});
|
|
|
|
function scan(signal) {
|
|
// What the scan found is logged at info level, which is not under test.
|
|
jest.spyOn(console, "log").mockImplementation(() => {});
|
|
const { xpub } = hdWalletFromMnemonic(generateMnemonic());
|
|
return scanForAddresses(xpub, RPC_URL, "mainnet", signal);
|
|
}
|
|
|
|
test("a scan failure while the popup is open is reported", async () => {
|
|
await scan(new AbortController().signal);
|
|
expect(
|
|
logged.some((line) => line.includes("scanForAddresses check failed")),
|
|
).toBe(true);
|
|
});
|
|
|
|
test("a scan failure once the popup has closed is not", async () => {
|
|
const pageClosed = new AbortController();
|
|
pageClosed.abort();
|
|
await scan(pageClosed.signal);
|
|
expect(logged).toEqual([]);
|
|
});
|