fix: the private key export screen opens again in the same popup session #478
@@ -45,6 +45,15 @@ but the review is broader than any of them.
|
|||||||
|
|
||||||
# Completed Steps
|
# 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
|
- 2026-10-06: The canonical files are re-vendored from `sneak/prompts` at
|
||||||
`dd4027b` ([#472](https://git.eeqj.de/sneak/AutistMask/issues/472)). The
|
`dd4027b` ([#472](https://git.eeqj.de/sneak/AutistMask/issues/472)). The
|
||||||
`Dockerfile` has separate `lint` and `test` phases, and its last stage depends
|
`Dockerfile` has separate `lint` and `test` phases, and its last stage depends
|
||||||
|
|||||||
@@ -391,14 +391,7 @@
|
|||||||
></div>
|
></div>
|
||||||
<h2 class="font-bold mb-1">Export Private Key</h2>
|
<h2 class="font-bold mb-1">Export Private Key</h2>
|
||||||
<p class="text-xs mb-1" id="export-privkey-title"></p>
|
<p class="text-xs mb-1" id="export-privkey-title"></p>
|
||||||
<div class="text-xs mb-3">
|
<div id="export-privkey-address" class="text-xs mb-3"></div>
|
||||||
<span id="export-privkey-dot"></span>
|
|
||||||
<span
|
|
||||||
id="export-privkey-address"
|
|
||||||
class="cursor-pointer"
|
|
||||||
title="Click to copy"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
<p class="text-xs mb-3 text-muted">
|
<p class="text-xs mb-3 text-muted">
|
||||||
Warning: anyone with this private key can access and
|
Warning: anyone with this private key can access and
|
||||||
transfer all funds from this address. Never share it.
|
transfer all funds from this address. Never share it.
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ function show(walletIdx, addrIdx) {
|
|||||||
|
|
||||||
$("export-privkey-title").textContent =
|
$("export-privkey-title").textContent =
|
||||||
wallet.name + " — Address " + (addrIdx + 1);
|
wallet.name + " — Address " + (addrIdx + 1);
|
||||||
const addrContainer = $("export-privkey-dot").parentElement;
|
const addrContainer = $("export-privkey-address");
|
||||||
addrContainer.innerHTML = renderAddressHtml(addr.address);
|
addrContainer.innerHTML = renderAddressHtml(addr.address);
|
||||||
attachCopyHandlers(addrContainer);
|
attachCopyHandlers(addrContainer);
|
||||||
|
|
||||||
|
|||||||
+2
-4
@@ -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) => {
|
test("leaving while the decrypt is in flight reveals no private key (#253)", async (env) => {
|
||||||
try {
|
try {
|
||||||
await leavePrivkeyScreen(env.page);
|
await leavePrivkeyScreen(env.page);
|
||||||
// The export screen cannot yet be opened twice in one popup session
|
// The second open in this popup session
|
||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/460), so this second
|
// (https://git.eeqj.de/sneak/AutistMask/issues/460).
|
||||||
// open gets a fresh one.
|
|
||||||
await reopenPopup(env, "main");
|
|
||||||
await openPrivkeyScreen(env.page);
|
await openPrivkeyScreen(env.page);
|
||||||
await env.page.fill("#export-privkey-password", PASSWORD);
|
await env.page.fill("#export-privkey-password", PASSWORD);
|
||||||
const inFlight = await env.page.evaluate(() => {
|
const inFlight = await env.page.evaluate(() => {
|
||||||
|
|||||||
@@ -58,7 +58,20 @@ function makeElement(id, withParent) {
|
|||||||
remove: () => {},
|
remove: () => {},
|
||||||
querySelectorAll: () => [],
|
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;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +83,8 @@ function makeDocument() {
|
|||||||
// is the state a non-debug, non-testnet popup is in.
|
// is the state a non-debug, non-testnet popup is in.
|
||||||
if (id === "debug-banner") return null;
|
if (id === "debug-banner") return null;
|
||||||
if (!els.has(id)) els.set(id, makeElement(id, true));
|
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),
|
createElement: () => makeElement("created", false),
|
||||||
addEventListener: () => {},
|
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", () => {
|
describe("views the popup may reopen onto", () => {
|
||||||
// Restoring onto this screen would put a private key on display with no
|
// 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.
|
// password prompt in front of it, on a popup reopened by accident.
|
||||||
|
|||||||
Reference in New Issue
Block a user