fix: explain a rejected dust threshold instead of snapping back silently (closes #233)
All checks were successful
check / check (push) Successful in 27s
All checks were successful
check / check (push) Successful in 27s
The dust threshold field resynced to the stored value on a rejected input and said nothing, so the box changed to a different number with no reason given. It was the only validated input in the settings view that rejected without a message. Rejected input now shows one full sentence naming the constraint, using the flash line already used by the RPC and Blockscout validation in the same file. No layout shift, measured rather than asserted: #flash-msg reserves exactly one line at text-xs (min-h-[1.25rem], 20px), so the message has to fit one line or it wraps and pushes the settings view down. "Enter a whole number of gwei, zero or greater." renders at 20px at the documented 360x600 popup width, identical to the empty line, with the settings view and the threshold field at the same document position either way. Hex and exponent notation are rejected rather than accepted. Number() reads 0x10 as 16 and 1e3 as 1000, which the earlier parseInt did not, and storing either would put a number in the field that the user never typed - the same silent substitution the message exists to end. Accepted input is plain decimal digits only; the field is inputmode="numeric" and the unit is printed beside it. The parse moves to src/popup/dustThreshold.js, pure and unit tested, with the message beside it so there is one wording. Unit tests cover the accepted set, the rejected notations, and that a rejection flashes the message and stores nothing while a valid value stores and stays quiet. The layout assertion lives in the e2e suite because it needs a layout engine: jest runs on the node environment, where every rendered height is zero, so no unit test can see the message wrap. tests/e2e/run.js drives the real popup in the pinned Playwright container, types a rejected value, measures the flash line filled against the same line empty, and fails if the message grows past one line - verified by lengthening it and watching the test go red.
This commit is contained in:
@@ -9,6 +9,10 @@ const {
|
||||
pushCurrentView,
|
||||
} = require("./helpers");
|
||||
const { applyTheme } = require("../theme");
|
||||
const {
|
||||
DUST_THRESHOLD_MESSAGE,
|
||||
parseDustThresholdGwei,
|
||||
} = require("../dustThreshold");
|
||||
const { state, saveState, currentNetwork } = require("../../shared/state");
|
||||
const { NETWORKS, SUPPORTED_CHAIN_IDS } = require("../../shared/networks");
|
||||
const { onChainSwitch } = require("../../shared/chainSwitch");
|
||||
@@ -329,13 +333,14 @@ function init(ctx) {
|
||||
|
||||
$("settings-dust-threshold").value = state.dustThresholdGwei;
|
||||
$("settings-dust-threshold").addEventListener("change", async () => {
|
||||
const raw = $("settings-dust-threshold").value.trim();
|
||||
const val = Number(raw);
|
||||
// 0 is accepted and means "hide nothing". Empty, negative,
|
||||
// fractional and non-numeric input is rejected outright rather than
|
||||
// coerced, and the field is put back to the stored threshold so it
|
||||
// never shows a value the wallet is not using.
|
||||
if (raw !== "" && Number.isInteger(val) && val >= 0) {
|
||||
const val = parseDustThresholdGwei($("settings-dust-threshold").value);
|
||||
// Rejected input is never coerced. The field is put back to the
|
||||
// stored threshold so it never shows a value the wallet is not
|
||||
// using, and the message says what the field wants so the snap-back
|
||||
// is explained rather than silent.
|
||||
if (val === null) {
|
||||
showFlash(DUST_THRESHOLD_MESSAGE);
|
||||
} else {
|
||||
state.dustThresholdGwei = val;
|
||||
await saveState();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user