test: the popup boot in the jest suite sends no request to the internet (closes #559)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run

bootPopup() in tests/support/popupBoot.js mocked the three shared
modules that reach the network, but a boot onto the transaction screen
still asked Blockscout for its transaction, and a boot onto the
confirmation screen looked the recipient up on Etherscan, both through
the real fetch(). tests/persistedFieldContract.test.js boots onto both,
so every run sent those requests and logged their answers after its
tests had finished. bootPopup() now replaces fetch() with the throwing
stub the rest of the suite uses, and cleanupPopup() puts it back.
tests/popupBoot.test.js checks that both requests reach the stub.

Model: opus-5-5
This commit is contained in:
2026-10-19 05:23:14 +00:00
parent b5d2cedb57
commit 9fb15302eb
2 changed files with 58 additions and 2 deletions
+45
View File
@@ -9,6 +9,7 @@ const {
bootPopup,
cleanupPopup,
unversionedValidProfile,
ADDRESS,
} = require("./support/popupBoot");
const FLASH_MS = 10;
@@ -40,3 +41,47 @@ test("a flash message's timer does not fire after cleanupPopup()", async () => {
await wait(FLASH_MS * 5);
});
// A boot onto a screen that sends requests of its own: they reach the boot's
// fetch() stub, never the internet
// (https://git.eeqj.de/sneak/AutistMask/issues/559).
function restoringOnto(view, viewData) {
return unversionedValidProfile({
currentView: view,
selectedWallet: 0,
selectedAddress: 0,
viewStack: ["main"],
viewData,
});
}
test("the transaction screen's Blockscout request reaches the stub", async () => {
const page = await bootPopup(
restoringOnto("transaction", {
tx: { hash: "0x1", from: ADDRESS, to: ADDRESS },
}),
);
expect(page.visibleViews()).toEqual(["transaction"]);
expect(globalThis.fetch.mock.calls.map((call) => call[0])).toEqual([
"https://eth.blockscout.com/api/v2/transactions/0x1",
]);
});
test("the confirmation screen's Etherscan lookup reaches the stub", async () => {
const page = await bootPopup(
restoringOnto("confirm-tx", {
pendingTx: {
token: "ETH",
from: ADDRESS,
to: ADDRESS,
amount: "1",
},
}),
);
expect(page.visibleViews()).toEqual(["confirm-tx"]);
expect(globalThis.fetch.mock.calls.map((call) => call[0])).toEqual([
"https://etherscan.io/address/" + ADDRESS,
]);
});
+13 -2
View File
@@ -45,6 +45,7 @@ const realSetInterval = globalThis.setInterval;
const realClearInterval = globalThis.clearInterval;
const realSetTimeout = globalThis.setTimeout;
const realClearTimeout = globalThis.clearTimeout;
const realFetch = globalThis.fetch;
// Every timeout set since the last cleanupPopup() that has neither fired nor
// been cleared. cleanupPopup() clears them all.
@@ -260,8 +261,9 @@ async function settle() {
async function bootPopup(stored, options) {
jest.resetModules();
// The three modules that reach the network. None is on the path under
// test; all would make the suite hit the internet.
// The three shared modules that reach the network. None is on the path
// under test; all would make the suite hit the internet. The requests the
// views send themselves meet the fetch() stub below.
jest.doMock("../../src/shared/prices", () => ({
prices: {},
refreshPrices: jest.fn(async () => {}),
@@ -334,6 +336,14 @@ async function bootPopup(stored, options) {
windowListeners[name].push(fn);
},
};
// Every fetch() the page makes fails, as it does elsewhere in the suite,
// instead of reaching the internet: the transaction screen asks Blockscout
// for its transaction and the confirmation screen looks the recipient up
// on Etherscan. A new stub per boot, so its calls are that boot's
// requests. Put back by cleanupPopup().
globalThis.fetch = jest.fn(() => {
throw new Error("tests must not perform network requests");
});
// The ten-second refresh init() starts, and a transaction wait's timers,
// would outlive the test. So every interval is recorded rather than
// started, clearInterval() removes it as a browser would, and tick() below
@@ -435,6 +445,7 @@ function cleanupPopup() {
pendingTimeouts.clear();
globalThis.setTimeout = realSetTimeout;
globalThis.clearTimeout = realClearTimeout;
globalThis.fetch = realFetch;
}
module.exports = {