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 stops the ten-second refresh, runs the leave cleanup of the current screen, and raises the recovery screen. From then on showView() shows nothing else in that popup, so a transaction wait or a later save cannot take the user off it or clear an export or a typed confirmation. That is held in memory, never as the saved current view, so a popup opened after the record is erased elsewhere opens normally. Any other failed save keeps the "NOT SAVED" banner. The popup test harness now honours clearInterval(). Model: opus-5-5
This commit was merged in pull request #439.
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