fix: render the view "Back" lands on after the popup is reopened (closes #268)
All checks were successful
check / check (push) Successful in 44s
All checks were successful
check / check (push) Successful in 44s
goBack() only unhid its target. A reopened popup renders the wallet list and the one view restoreView() lands on, so every other view is still the blank static template from index.html: pressing Back from Settings onto an address showed an empty address line and no balances, and the same held for address-token, receive, confirm-tx and transaction. The per-view dispatch and its data guards move out of restoreView() into src/popup/viewRouter.js, and goBack() now routes a popped view through the same code by way of a renderer index.js registers with setBackRenderer(). A view whose backing state is gone falls back to Home the way the restore does, rather than showing an empty template. The renderer declines any view outside RESTORABLE_VIEWS, so goBack() unhides it as before: the restored stack is filtered against that set, so such a view can only be on the stack from the current page load, where forward navigation rendered it on the way in. Forward navigation is untouched and nothing renders twice. tests/backNavigation.test.js drives the real goBack() over the reproduction and over each of address-token, receive, confirm-tx and transaction, with and without their backing state, and pins that a live-session view is still only unhidden. tests/e2e/run.js adds two cases against the real popup in a real browser — a real close and reopen, then Back onto the address screen and onto Receive — because make check cannot see a blank view. Both were demonstrated failing against the unmodified sources: the address line read "" where it should have read the address.
This commit is contained in:
130
tests/e2e/run.js
130
tests/e2e/run.js
@@ -406,6 +406,136 @@ test("reopening the popup never lands on the phrase screen (#161)", async (env)
|
||||
assertWiped(st, env.phrase, "after reopening the popup");
|
||||
});
|
||||
|
||||
// ------------------------------- Back after reopening the popup (#268)
|
||||
|
||||
// A reopened popup renders the wallet list and the view it restores onto,
|
||||
// and nothing else: every other screen is still the blank static template
|
||||
// from index.html. Back used to only unhide its target, which is why these
|
||||
// have to run against the real popup — the template is present and
|
||||
// well-formed, so only its emptiness distinguishes the defect, and only a
|
||||
// real reopen produces it.
|
||||
|
||||
// Everything the address screen must have on it, read out of the DOM.
|
||||
function addressScreenState(page) {
|
||||
return page.evaluate(() => {
|
||||
const line = document.getElementById("address-line");
|
||||
const balances = document.getElementById("address-balances");
|
||||
return {
|
||||
hidden: document
|
||||
.getElementById("view-address")
|
||||
.classList.contains("hidden"),
|
||||
line: line ? line.innerText.trim() : "",
|
||||
balances: balances ? balances.innerText.trim() : "",
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Close and reopen the page rather than reload it: that is what the toolbar
|
||||
// popup does, and it is the only thing that produces the unrendered views.
|
||||
async function reopenPopup(env, restoredView) {
|
||||
await env.page.close();
|
||||
env.page = await openPopup(env.ctx, env.popupUrl);
|
||||
await visible(env.page, restoredView);
|
||||
}
|
||||
|
||||
// The reproduction from the issue, step for step.
|
||||
test("Back after reopening the popup renders the address screen (#268)", async (env) => {
|
||||
await openAddressDetail(env.page);
|
||||
const before = await addressScreenState(env.page);
|
||||
assert(
|
||||
before.line.length > 0,
|
||||
"the address screen was blank to begin with",
|
||||
);
|
||||
|
||||
await env.page.click("#btn-settings");
|
||||
await visible(env.page, "#view-settings");
|
||||
|
||||
await reopenPopup(env, "#view-settings");
|
||||
|
||||
await env.page.click("#btn-settings-back");
|
||||
await visible(env.page, "#view-address");
|
||||
|
||||
const after = await addressScreenState(env.page);
|
||||
assert(
|
||||
after.line === before.line,
|
||||
"the address line reads " +
|
||||
JSON.stringify(after.line) +
|
||||
", expected " +
|
||||
JSON.stringify(before.line),
|
||||
);
|
||||
assert(
|
||||
after.balances.includes("ETH"),
|
||||
"the balances read " + JSON.stringify(after.balances),
|
||||
);
|
||||
});
|
||||
|
||||
// The same defect one screen further in. Receive holds the address twice
|
||||
// over — as text and as the QR code the sender scans — and a blank one is
|
||||
// worse than a missing screen.
|
||||
// Everything the Receive screen must have on it. The QR code is read as
|
||||
// pixels, not as an element: the blank template carries the canvas too, a
|
||||
// default 300x150 one with nothing drawn on it and every pixel fully
|
||||
// transparent. A drawn QR paints an opaque background across the whole
|
||||
// canvas, so a single opaque pixel is the whole question.
|
||||
function receiveScreenState(page) {
|
||||
return page.evaluate(() => {
|
||||
const block = document.getElementById("receive-address-block");
|
||||
const canvas = document.getElementById("receive-qr");
|
||||
const px = canvas
|
||||
.getContext("2d")
|
||||
.getImageData(0, 0, canvas.width, canvas.height).data;
|
||||
let opaque = 0;
|
||||
for (let i = 3; i < px.length; i += 4) {
|
||||
if (px[i] > 0) opaque += 1;
|
||||
}
|
||||
return {
|
||||
address: block.dataset.full || "",
|
||||
text: block.innerText.trim(),
|
||||
qrOpaquePixels: opaque,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
test("Back after reopening the popup renders the Receive screen (#268)", async (env) => {
|
||||
await openAddressDetail(env.page);
|
||||
await env.page.click("#btn-receive");
|
||||
await visible(env.page, "#view-receive");
|
||||
const before = await receiveScreenState(env.page);
|
||||
assert(
|
||||
/^0x[0-9a-fA-F]{40}$/.test(before.address),
|
||||
"Receive showed no address to begin with: " +
|
||||
JSON.stringify(before.address),
|
||||
);
|
||||
|
||||
await env.page.click("#btn-settings");
|
||||
await visible(env.page, "#view-settings");
|
||||
|
||||
await reopenPopup(env, "#view-settings");
|
||||
|
||||
await env.page.click("#btn-settings-back");
|
||||
await visible(env.page, "#view-receive");
|
||||
|
||||
const shown = await receiveScreenState(env.page);
|
||||
assert(
|
||||
shown.address === before.address,
|
||||
"Receive shows " +
|
||||
JSON.stringify(shown.address) +
|
||||
", expected " +
|
||||
JSON.stringify(before.address),
|
||||
);
|
||||
assert(
|
||||
shown.text.includes(before.address),
|
||||
"the Receive address is not on screen: " + JSON.stringify(shown.text),
|
||||
);
|
||||
assert(shown.qrOpaquePixels > 0, "Receive shows an unpainted QR code");
|
||||
|
||||
// Leave the suite where it found it.
|
||||
await env.page.click("#btn-receive-back");
|
||||
await visible(env.page, "#view-address");
|
||||
await env.page.click("#btn-address-back");
|
||||
await visible(env.page, "#view-main");
|
||||
});
|
||||
|
||||
// -------------------------------------------- address removal (#162)
|
||||
|
||||
// Number of address rows across every wallet in the list, counted in the DOM
|
||||
|
||||
Reference in New Issue
Block a user