diff --git a/TODO.md b/TODO.md index eddcc3a..4bf1256 100644 --- a/TODO.md +++ b/TODO.md @@ -45,6 +45,16 @@ but the review is broader than any of them. # Completed Steps +- 2026-10-04: The error container on each dApp approval screen keeps its height + when an error appears + ([#297](https://git.eeqj.de/sneak/AutistMask/issues/297)). `#approve-tx-error` + and `#approve-sign-error` reserved 20px, but their border and padding took + 10px of it, so a one-line error grew them to 26px and pushed the buttons below + down 6px. They now reserve 30px. A new test in `tests/e2e/run.js` shows each + of the six password error containers on its own screen, empty and then with an + error, and fails if one changes height or the element below it moves. Some of + the longer messages these two containers can show still take two lines. + - 2026-10-04: A transaction with no `to` says "This transaction creates a new contract. It has no recipient." on its recipient line and in its transaction history row ([#250](https://git.eeqj.de/sneak/AutistMask/issues/250)). The diff --git a/src/popup/index.html b/src/popup/index.html index 741bc1f..554f6be 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -1633,7 +1633,7 @@
@@ -1710,7 +1710,7 @@
diff --git a/tests/e2e/run.js b/tests/e2e/run.js index c3b61de..1a256c1 100644 --- a/tests/e2e/run.js +++ b/tests/e2e/run.js @@ -1449,6 +1449,81 @@ test("an over-long flash message keeps to one line (#252)", async (env) => { } }); +// --------------------------------------- password error containers (#297) + +// Every screen that asks for a password reserves room for one line of error. +// The two on the dApp approval screens also have a border and padding, which +// that reserved height has to cover too. +const PASSWORD_ERROR_CONTAINERS = [ + "approve-tx-error", + "approve-sign-error", + "export-privkey-flash", + "show-phrase-flash", + "delete-wallet-flash", + "confirm-tx-password-error", +]; + +// Shows only the screen holding the container, then measures the container +// and the element below it empty and again filled the way showError() in +// src/popup/views/helpers.js fills it. Runs in the page. +function measurePasswordError(id) { + const container = document.getElementById(id); + const screen = container.closest(".view"); + for (const view of document.querySelectorAll(".view")) { + view.classList.toggle("hidden", view !== screen); + } + const below = container.nextElementSibling; + const measure = () => ({ + height: container.getBoundingClientRect().height, + belowTop: below.getBoundingClientRect().top + window.scrollY, + belowHeight: below.getBoundingClientRect().height, + }); + const empty = measure(); + container.textContent = "Please enter your password."; + container.style.visibility = "visible"; + const filled = measure(); + container.textContent = ""; + container.style.visibility = "hidden"; + return { empty, filled }; +} + +test("a password error moves nothing on any screen (#297)", async (env) => { + const page = await openPopup(env.ctx, env.popupUrl); + try { + await page.setViewportSize(POPUP_VIEWPORT); + for (const id of PASSWORD_ERROR_CONTAINERS) { + const { empty, filled } = await page.evaluate( + measurePasswordError, + id, + ); + assert( + empty.belowHeight > 0, + "nothing is shown below #" + id + ", so nothing was measured", + ); + assert( + filled.height === empty.height, + "#" + + id + + " is " + + empty.height + + "px empty and " + + filled.height + + "px with an error", + ); + assert( + filled.belowTop === empty.belowTop, + "the element below #" + + id + + " moved " + + (filled.belowTop - empty.belowTop) + + "px when the error appeared", + ); + } + } finally { + await page.close(); + } +}); + // --------------------------------------------- confirmation screen (#238) // // The screen that decides what gets signed. The arithmetic underneath it