fix: the private key export screen opens again in the same popup session #478

Merged
clawbot merged 1 commits from issue-460-export-privkey-reopen into next 2026-10-06 19:43:08 +02:00
5 changed files with 51 additions and 15 deletions
+9
View File
@@ -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
+1 -8
View File
@@ -391,14 +391,7 @@
></div>
<h2 class="font-bold mb-1">Export Private Key</h2>
<p class="text-xs mb-1" id="export-privkey-title"></p>
<div class="text-xs mb-3">
<span id="export-privkey-dot"></span>
<span
id="export-privkey-address"
class="cursor-pointer"
title="Click to copy"
></span>
</div>
<div id="export-privkey-address" class="text-xs mb-3"></div>
<p class="text-xs mb-3 text-muted">
Warning: anyone with this private key can access and
transfer all funds from this address. Never share it.
+1 -1
View File
@@ -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);
+2 -4
View File
@@ -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(() => {
+38 -2
View File
@@ -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.