test: drive the Settings screen in a browser and guard every popup element id (closes #229)
All checks were successful
check / check (push) Successful in 27s
e2e / e2e-chrome (push) Successful in 48s
e2e / e2e-firefox (push) Successful in 17s

Nothing exercised the Settings view in a browser, and jest runs in the
node environment with no DOM, so the densest run of $("...") lookups in
the codebase was unverified at runtime. A wrong id is valid JavaScript
naming a defined function: $() returns null and the next property access
throws, which inside a view's init() aborts the rest of the popup's
init() and leaves every screen blank.

Two halves, because they catch different things.

The e2e suite (tests/e2e/run.js) gains seven cases between the address
removal and dust threshold sections. They assert the About well and the
wallet list were actually written — show() populates those near its end,
only the debug well and the debug-mode checkbox follow, so reading them
back proves show() ran through to there rather than just far enough to
unhide the section — that the four Token Spam Protection controls are
real input[type=checkbox] elements defaulted on, and that the theme and
network selectors offer exactly the choices src/shared/networks.js and
index.html define.

What the selectors persist is asserted by a round trip through
NON-DEFAULT values: they are driven to dark and sepolia, the popup is
closed and reopened, both are read back, and both are then restored the
same way and reasserted after a second reopen. Neither value is the
first <option> of its <select>, which is the point — the first option is
what the DOM reports with no JavaScript having run at all, so asserting
it would pass just as happily against a Settings screen that assigned
nothing. One spam filter is likewise toggled off and back on across a
reopen each way. Those round trips run the change handler, saveState(),
loadState() and the assignments show() and init() make, rather than only
looking at the screen. Each group records a coverage key and a final
case demands the exact set, so a section that silently stopped running
reddens the suite instead of shrinking it.

show() no longer wraps its settings-network lookup in if (networkSelect),
and neither does init(): a null there was silently skipped, which is
exactly the failure this change exists to make loud.

tests/popupElementIds.test.js is the general half and needs no browser,
so jest picks it up and it runs in make check: every literal id reached
through $(), document.getElementById(), showError()/hideError() and
showView() must exist in src/popup/index.html, no id in index.html may
be defined twice, and the scan asserts it found the code and the markup
so it cannot pass by covering nothing. Only literal arguments are
resolvable statically; $(containerId) and a lookup naming the wrong
existing element are the browser suites' job, and README says so.

Demonstrated against four deliberate breaks. A typo'd id in settings.js
reddens both halves, the e2e run reporting "pageerror: Cannot set
properties of null (setting 'checked')" against its first test. A
handler bound to the wrong but existing element passes the static guard
and reddens only the new functional case. A typo in a view no browser
suite opens reddens only the static guard. Deleting either persisted
value assignment in settings.js — the theme one in init(), the network
one in show() — reddens the selector round trip and nothing else, each
one on its own.
This commit is contained in:
2026-08-17 06:21:00 +00:00
parent ab1c1846a7
commit 075590ed39
5 changed files with 581 additions and 13 deletions

28
TODO.md
View File

@@ -46,6 +46,34 @@ undefined identifiers, which is how
# Completed Steps
- 2026-08-17: The Settings screen is driven in a browser, and every element id
the popup looks up is checked statically. Nothing exercised Settings in the
e2e suite, and jest runs with no DOM, so the densest run of `$("...")` lookups
in the codebase was unverified at runtime. Seven new cases in
`tests/e2e/run.js` reach Settings, assert the About well and the wallet list
were actually written, assert the four Token Spam Protection checkboxes are
real checkboxes defaulted on, and assert the theme and network selectors offer
the choices `src/shared/networks.js` and `index.html` define. The selectors
are then driven to `dark` and `sepolia` — neither is the first `<option>`, so
neither can be read back from the markup with no JavaScript involved — and
reasserted after a popup reopen before being restored the same way, and one
spam filter is toggled off and back on across a reopen each way. Those round
trips run the change handler, `saveState()`, `loadState()` and the
`init()`/`show()` assignments rather than just looking at the screen. `show()`
no longer guards its `settings-network` lookup with `if (networkSelect)`: a
missing element must fail loudly, which is the whole failure mode this unit
exists to catch. Each group records a coverage key and a final case demands
the exact set, so a shortened or skipped section reddens the run instead of
shrinking it. `tests/popupElementIds.test.js` is the general half and runs in
`make check` with no browser: every literal id reached through `$()`,
`document.getElementById()`, `showError()`/`hideError()` and `showView()` must
exist in `src/popup/index.html`, which no id in `index.html` may define twice.
Demonstrated on four deliberate breaks — a typo'd id (both halves red), a
handler bound to the wrong but existing element (only the functional e2e case
red), a typo in a view no browser suite opens (only the static guard red), and
the deletion of both persisted-value assignments in `settings.js` (only the
selector round-trip case red)
([#229](https://git.eeqj.de/sneak/AutistMask/issues/229)).
- 2026-08-17: One wording for an empty password field on every screen that asks
for one. The private key export screen said "Password is required." where the
other five say "Please enter your password.", the same one-condition-two-