test: no timer a popup test started fires after cleanupPopup() (closes #578) #582
@@ -61,16 +61,12 @@ async function importOn(page, { tab, field, secret }) {
|
||||
await page.click("btn-add-wallet-confirm");
|
||||
}
|
||||
|
||||
// Fake timers hold the timer of the flash message an import ends with, which
|
||||
// would otherwise fire after cleanupPopup() has removed the page and throw.
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
chainBalance = "0.0";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupPopup();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
describe("a popup that opened on Welcome", () => {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// A timer the popup started and that fires after cleanupPopup() has removed
|
||||
// the page throws "document is not defined", and the throw fails whatever test
|
||||
// the jest worker is running by then, in this file or another
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/578). The flash message's
|
||||
// timer is the one that did it. A throw from a timer during a test fails that
|
||||
// test, so each test here passes only if nothing throws.
|
||||
|
||||
const {
|
||||
bootPopup,
|
||||
cleanupPopup,
|
||||
unversionedValidProfile,
|
||||
} = require("./support/popupBoot");
|
||||
|
||||
const FLASH_MS = 10;
|
||||
|
||||
function wait(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
cleanupPopup();
|
||||
});
|
||||
|
||||
test("a flash message clears while the page is up", async () => {
|
||||
const page = await bootPopup(unversionedValidProfile());
|
||||
const { showFlash } = require("../src/popup/views/helpers");
|
||||
showFlash("Copied.", FLASH_MS);
|
||||
expect(page.text("flash-msg")).toBe("Copied.");
|
||||
|
||||
await wait(FLASH_MS * 5);
|
||||
|
||||
expect(page.text("flash-msg")).toBe("");
|
||||
});
|
||||
|
||||
test("a flash message's timer does not fire after cleanupPopup()", async () => {
|
||||
await bootPopup(unversionedValidProfile());
|
||||
const { showFlash } = require("../src/popup/views/helpers");
|
||||
showFlash("Copied.", FLASH_MS);
|
||||
cleanupPopup();
|
||||
|
||||
await wait(FLASH_MS * 5);
|
||||
});
|
||||
@@ -43,6 +43,12 @@ jest.doMock("ethereum-blockies-base64", () => makeBlockie);
|
||||
// Taken before any boot replaces them; see bootPopup().
|
||||
const realSetInterval = globalThis.setInterval;
|
||||
const realClearInterval = globalThis.clearInterval;
|
||||
const realSetTimeout = globalThis.setTimeout;
|
||||
const realClearTimeout = globalThis.clearTimeout;
|
||||
|
||||
// Every timeout set since the last cleanupPopup() that has neither fired nor
|
||||
// been cleared. cleanupPopup() clears them all.
|
||||
const pendingTimeouts = new Set();
|
||||
|
||||
const POPUP_HTML = fs.readFileSync(
|
||||
path.join(__dirname, "..", "..", "src", "popup", "index.html"),
|
||||
@@ -342,6 +348,24 @@ async function bootPopup(stored, options) {
|
||||
globalThis.clearInterval = (id) => {
|
||||
intervals.delete(id);
|
||||
};
|
||||
// Timeouts, such as the one that clears a flash message, run for real, but
|
||||
// each is recorded until it fires or is cleared, so cleanupPopup() can
|
||||
// clear the ones still pending. One that fired after the page was removed
|
||||
// would throw "document is not defined" into whatever test the worker runs
|
||||
// by then. jest.useFakeTimers() in a test does not reach them. Put back by
|
||||
// cleanupPopup().
|
||||
globalThis.setTimeout = (fn, ms, ...args) => {
|
||||
const timer = realSetTimeout(() => {
|
||||
pendingTimeouts.delete(timer);
|
||||
fn(...args);
|
||||
}, ms);
|
||||
pendingTimeouts.add(timer);
|
||||
return timer;
|
||||
};
|
||||
globalThis.clearTimeout = (timer) => {
|
||||
pendingTimeouts.delete(timer);
|
||||
realClearTimeout(timer);
|
||||
};
|
||||
|
||||
require("../../src/popup/index");
|
||||
|
||||
@@ -407,6 +431,10 @@ function cleanupPopup() {
|
||||
delete globalThis.window;
|
||||
globalThis.setInterval = realSetInterval;
|
||||
globalThis.clearInterval = realClearInterval;
|
||||
for (const timer of pendingTimeouts) realClearTimeout(timer);
|
||||
pendingTimeouts.clear();
|
||||
globalThis.setTimeout = realSetTimeout;
|
||||
globalThis.clearTimeout = realClearTimeout;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user