fix: an open popup moves to the recovery screen when its profile becomes unreadable (closes #373)
A popup already open when the stored profile became unreadable stayed on the last good profile until reopened. Every save already runs the check loadState() runs at open; a save refused by it now raises the recovery screen, stops the ten-second refresh, and passes the screen to showView(), which runs the leave cleanup of the screen it replaces and records it as the current view. From then on showView() shows nothing else, so a transaction wait or a later save cannot take the user off it or clear an export or a typed confirmation. Any other failed save keeps the "NOT SAVED" banner. The popup test harness now honours clearInterval(). Model: opus-5-5
This commit is contained in:
@@ -40,6 +40,10 @@ jest.doMock("libsodium-wrappers-sumo", () => sodium);
|
||||
jest.doMock("qrcode", () => QRCode);
|
||||
jest.doMock("ethereum-blockies-base64", () => makeBlockie);
|
||||
|
||||
// Taken before any boot replaces them; see bootPopup().
|
||||
const realSetInterval = globalThis.setInterval;
|
||||
const realClearInterval = globalThis.clearInterval;
|
||||
|
||||
const POPUP_HTML = fs.readFileSync(
|
||||
path.join(__dirname, "..", "..", "src", "popup", "index.html"),
|
||||
"utf8",
|
||||
@@ -292,9 +296,20 @@ async function bootPopup(stored, options) {
|
||||
}),
|
||||
addEventListener: () => {},
|
||||
};
|
||||
// The 10s refresh loop init() starts would outlive the test.
|
||||
const realSetInterval = globalThis.setInterval;
|
||||
globalThis.setInterval = () => 0;
|
||||
// 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
|
||||
// runs the ones still set. Put back by cleanupPopup().
|
||||
const intervals = new Map();
|
||||
let lastId = 0;
|
||||
globalThis.setInterval = (fn) => {
|
||||
lastId += 1;
|
||||
intervals.set(lastId, fn);
|
||||
return lastId;
|
||||
};
|
||||
globalThis.clearInterval = (id) => {
|
||||
intervals.delete(id);
|
||||
};
|
||||
|
||||
require("../../src/popup/index");
|
||||
|
||||
@@ -316,8 +331,6 @@ async function bootPopup(stored, options) {
|
||||
}
|
||||
await settle();
|
||||
|
||||
globalThis.setInterval = realSetInterval;
|
||||
|
||||
return {
|
||||
storage,
|
||||
document,
|
||||
@@ -337,6 +350,12 @@ async function bootPopup(stored, options) {
|
||||
for (const fn of fns) await fn();
|
||||
await settle();
|
||||
},
|
||||
// Every interval still set runs once: the ten-second refresh, and a
|
||||
// transaction wait's receipt poll and elapsed counter while one runs.
|
||||
tick: async () => {
|
||||
for (const fn of intervals.values()) await fn();
|
||||
await settle();
|
||||
},
|
||||
settle,
|
||||
// The view ids whose section is not hidden, as the audit measured them.
|
||||
visibleViews: () => {
|
||||
@@ -354,6 +373,8 @@ function cleanupPopup() {
|
||||
delete globalThis.chrome;
|
||||
delete globalThis.document;
|
||||
delete globalThis.window;
|
||||
globalThis.setInterval = realSetInterval;
|
||||
globalThis.clearInterval = realClearInterval;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user