The transaction lists on the address and token screens, the address scan after a wallet is created, and the endpoint checks in Settings now check the signal the popup aborts on pagehide before reporting a failed request, as the home screen and the balance refresh already do. scanForAddresses() takes the signal ahead of its gap limit. End-to-end tests reload the popup on the address screen with Blockscout held, and during the address scan with its requests held. Jest tests show the transaction lists, the scan and the Settings checks report a real failure and stay silent once the popup has closed. The transaction detail and confirmation screens are left out: they do not keep the popup context. Model: opus-5-5
128 lines
3.9 KiB
JavaScript
128 lines
3.9 KiB
JavaScript
// The address and token screens do not report a transaction list the popup's
|
|
// own closing cancelled, and still report one that failed while the popup was
|
|
// open (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 the explorer request here always fails
|
|
// that way, and only the signal the popup aborts on pagehide tells the two
|
|
// cases apart. Driven against a minimal DOM stub in the shape
|
|
// tests/timestampDisplay.test.js uses.
|
|
|
|
jest.mock("../src/shared/transactions", () => ({
|
|
...jest.requireActual("../src/shared/transactions"),
|
|
fetchRecentTransactions: async () => {
|
|
throw new TypeError("Failed to fetch");
|
|
},
|
|
}));
|
|
|
|
globalThis.chrome = {
|
|
storage: { local: { get: async () => ({}), set: async () => {} } },
|
|
};
|
|
|
|
const { state } = require("../src/shared/state");
|
|
const addressDetail = require("../src/popup/views/addressDetail");
|
|
const addressToken = require("../src/popup/views/addressToken");
|
|
|
|
const ADDRESS = "0x1111111111111111111111111111111111111111";
|
|
|
|
function makeElement(id) {
|
|
const el = {
|
|
id,
|
|
textContent: "",
|
|
value: "",
|
|
innerHTML: "",
|
|
style: {},
|
|
dataset: {},
|
|
classList: {
|
|
add: () => {},
|
|
remove: () => {},
|
|
contains: () => false,
|
|
toggle: () => false,
|
|
},
|
|
addEventListener: () => {},
|
|
querySelectorAll: () => [],
|
|
appendChild: () => {},
|
|
};
|
|
// Views reach for .parentElement to hide whole sections.
|
|
Object.defineProperty(el, "parentElement", {
|
|
get: () => node(id + "-parent"),
|
|
});
|
|
return el;
|
|
}
|
|
|
|
function makeDocument() {
|
|
const els = new Map();
|
|
return {
|
|
getElementById(id) {
|
|
// The debug banner is created on demand by helpers.js; absent
|
|
// is the state a non-debug, non-testnet popup is in.
|
|
if (id === "debug-banner") return null;
|
|
if (!els.has(id)) els.set(id, makeElement(id));
|
|
return els.get(id);
|
|
},
|
|
createElement: () => makeElement("created"),
|
|
addEventListener: () => {},
|
|
body: { prepend: () => {} },
|
|
};
|
|
}
|
|
|
|
function node(id) {
|
|
return globalThis.document.getElementById(id);
|
|
}
|
|
|
|
let logged;
|
|
|
|
beforeEach(() => {
|
|
logged = [];
|
|
jest.spyOn(console, "error").mockImplementation((...args) => {
|
|
logged.push(args.map(String).join(" "));
|
|
});
|
|
globalThis.document = makeDocument();
|
|
globalThis.window = { location: { search: "" } };
|
|
state.wallets = [
|
|
{
|
|
name: "Main",
|
|
type: "key",
|
|
addresses: [{ address: ADDRESS, balance: "0.0000" }],
|
|
},
|
|
];
|
|
state.trackedTokens = [];
|
|
state.viewStack = [];
|
|
state.selectedWallet = 0;
|
|
state.selectedAddress = 0;
|
|
state.selectedToken = "ETH";
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
describe.each([
|
|
["the address screen", addressDetail, "tx-list"],
|
|
["the token screen", addressToken, "address-token-tx-list"],
|
|
])("the transaction list on %s", (_name, view, listId) => {
|
|
// Open the screen and wait for its transaction list to fail.
|
|
async function open(pageClosed) {
|
|
view.init({ pageClosed });
|
|
view.show();
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
}
|
|
|
|
test("a failure while the popup is open is reported", async () => {
|
|
await open(new AbortController().signal);
|
|
expect(logged).toEqual([
|
|
"[AutistMask] loadTransactions failed: Failed to fetch",
|
|
]);
|
|
expect(node(listId).innerHTML).toContain(
|
|
"Failed to load transactions.",
|
|
);
|
|
});
|
|
|
|
test("a failure once the popup has closed is not", async () => {
|
|
const pageClosed = new AbortController();
|
|
pageClosed.abort();
|
|
await open(pageClosed.signal);
|
|
expect(logged).toEqual([]);
|
|
});
|
|
});
|