diff --git a/TODO.md b/TODO.md index be03229..646cb03 100644 --- a/TODO.md +++ b/TODO.md @@ -45,6 +45,15 @@ but the review is broader than any of them. # Completed Steps +- 2026-10-06: The private key export screen opens again in the same popup + session ([#460](https://git.eeqj.de/sneak/AutistMask/issues/460)). `show()` + found the address line through the element inside it, which its own rendering + replaced, so the second open threw before it navigated. The line now carries + the `export-privkey-address` id itself. `tests/exportPrivkey.test.js` opens + the screen twice, its DOM stub now takes an element out of the document when + its parent's contents are replaced, and the `#253` e2e case no longer reopens + the popup before its second open. + - 2026-10-06: The canonical files are re-vendored from `sneak/prompts` at `dd4027b` ([#472](https://git.eeqj.de/sneak/AutistMask/issues/472)). The `Dockerfile` has separate `lint` and `test` phases, and its last stage depends diff --git a/src/popup/index.html b/src/popup/index.html index 35e2d4c..f92c9c5 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -391,14 +391,7 @@ >
Warning: anyone with this private key can access and transfer all funds from this address. Never share it. diff --git a/src/popup/views/exportPrivkey.js b/src/popup/views/exportPrivkey.js index a3113ae..5daa7ed 100644 --- a/src/popup/views/exportPrivkey.js +++ b/src/popup/views/exportPrivkey.js @@ -98,7 +98,7 @@ function show(walletIdx, addrIdx) { $("export-privkey-title").textContent = wallet.name + " — Address " + (addrIdx + 1); - const addrContainer = $("export-privkey-dot").parentElement; + const addrContainer = $("export-privkey-address"); addrContainer.innerHTML = renderAddressHtml(addr.address); attachCopyHandlers(addrContainer); diff --git a/tests/e2e/run.js b/tests/e2e/run.js index b8eec66..47f76a0 100644 --- a/tests/e2e/run.js +++ b/tests/e2e/run.js @@ -1104,10 +1104,8 @@ test("leaving by the settings gear wipes the private key (#253)", async (env) => test("leaving while the decrypt is in flight reveals no private key (#253)", async (env) => { try { await leavePrivkeyScreen(env.page); - // The export screen cannot yet be opened twice in one popup session - // (https://git.eeqj.de/sneak/AutistMask/issues/460), so this second - // open gets a fresh one. - await reopenPopup(env, "main"); + // The second open in this popup session + // (https://git.eeqj.de/sneak/AutistMask/issues/460). await openPrivkeyScreen(env.page); await env.page.fill("#export-privkey-password", PASSWORD); const inFlight = await env.page.evaluate(() => { diff --git a/tests/exportPrivkey.test.js b/tests/exportPrivkey.test.js index 06fef69..48eb0a3 100644 --- a/tests/exportPrivkey.test.js +++ b/tests/exportPrivkey.test.js @@ -58,7 +58,20 @@ function makeElement(id, withParent) { remove: () => {}, querySelectorAll: () => [], }; - el.parentElement = withParent ? makeElement(id + "-parent", false) : null; + el.parentElement = null; + if (withParent) { + // As in a browser, replacing the parent's contents takes this + // element out of the document: getElementById no longer finds it. + el.parentElement = makeElement(id + "-parent", false); + let html = ""; + Object.defineProperty(el.parentElement, "innerHTML", { + get: () => html, + set: (value) => { + html = value; + el.removed = true; + }, + }); + } return el; } @@ -70,7 +83,8 @@ function makeDocument() { // 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, true)); - return els.get(id); + const el = els.get(id); + return el.removed ? null : el; }, createElement: () => makeElement("created", false), addEventListener: () => {}, @@ -300,6 +314,28 @@ describe("leaving the screen after the key is on it", () => { }); }); +describe("opening the screen again in the same popup session", () => { + // https://git.eeqj.de/sneak/AutistMask/issues/460: show() found the + // address line through an element inside it, which its own rendering + // deleted, so the second open threw before it navigated. + test("shows it for the address chosen the second time", async () => { + const { state, exportPrivkey } = load(); + exportPrivkey.show(0, 0); + await click("btn-export-privkey-back"); + expect(state.currentView).toBe("address"); + + exportPrivkey.show(0, 1); + + expect(state.currentView).toBe(VIEW); + expect(node("export-privkey-title").textContent).toBe( + "Wallet 1 — Address 2", + ); + expect(node("export-privkey-address").innerHTML).toContain( + "0x" + "22".repeat(20), + ); + }); +}); + describe("views the popup may reopen onto", () => { // Restoring onto this screen would put a private key on display with no // password prompt in front of it, on a popup reopened by accident.