Browser Session and Terminal Selector Tests
Run from the repository root:
python3 tests/web_ui_session/run.py
Requires a host C compiler, Python 3, and Node with Fetch/Response/ReadableStream support (Node 18+). All compiler outputs and rendered scripts are temporary; no firmware build, generated assets, or device writes are performed.
The runner compiles production src/web_ui.c with HTTPD and vendored-asset data
doubles. It reuses the HTTPD stub text from tests/web_login_ui/run.py, without
importing/executing that runner. Node executes the actual C-rendered application
and inline asset-failure script, not a separately maintained implementation.
Historical 8D.17 run: 126 behavior groups, plus production renderer/HTML/CSP checks. Seven new quick groups cover mouse/focus/touch activation, single DOM ownership, focused-editor hover stability, Escape/outside dismissal, late reads/expiry, terminal input/drain isolation, full-page draft/promotion preservation, explicit Apply/Save and pending non-replay, Network secret exclusion/strict schema/401. Element focus/containment/hover and events are doubles, not browser event-dispatch proof.
Optional geometry: WEB_UI_CHROMIUM=/usr/bin/chromium-browser python3 tests/web_ui_session/run.py. The fixture covers 36 cases at 320/600/900/1200px: full settings and quick panels plus dashboard card hit-testing, overflow/ellipsis, compact controls, status visibility, and full-page restoration. Quick panels use height 360px. The 2026-09-14 cosmetic update passed these Chromium fixtures and all 161 browser behavior groups; fixtures are not live-device validation. See contextual-control contracts, acceptance evidence and limits, and regression procedures.
Coverage:
- Resource selection, NULL/invalid input, setter/send failure propagation, eight-header ceiling, no-store document/application, unchanged vendor caching, nosniff/no-referrer/frame denial, exact inline-loader CSP hash and login fallback.
- Session validation before initial/retried/restored connections; memory-only CSRF header and empty ticket/logout bodies; safe-text username/absolute expiry.
- 401 shutdown and navigation once; manual recovery on 403; bounded Retry-After display/backoff for capacity; network errors never assert successful logout.
- Confirmed 204 logout, lost response confirmed by session 401, uncertain logout, cancellation, explicit recovery, and stale session/ticket/status/logout/WS work.
- Pagehide/bfcache restoration, late response bodies, and superseded session checks.
- Existing writer controls, 1,024-byte binary input chunks, raw binary output, observer input gating, and explicit Disconnect pausing reconnect.
- Authentication/ticket response cap 512 bytes, existing status cap 3,072 bytes, 15-second request deadline, single status request in flight, bounded retry delay, and unchanged 5,000-line terminal scrollback.
- Admin-only navigation, explicit protected-ticket open, 20 switches preserving serial socket/client/writer IDs, separate output and selected-only input.
- Independent 64 KiB pending output limits, visible dropped-byte counters and resumed hidden output draining; 512-byte admin input frames and overflow close.
- Admin close/reopen/remote exit isolation, 401/logout/expiry/pagehide cancellation, late tickets/callbacks, handshake timeout, selected resize and three listener cleanup/restore cycles.
- Session identity changes (username, role or session-stable CSRF) force a clean document before view adoption, close old admin and prevent replacement-session logout; same-session restore retains both scrollbacks behind validation gating.
- Undefined initial dimensions recover at unchanged bounds; failed fits never populate the cache, readiness retries stop at three, and teardown fences stale callbacks even after restore.
- Read-only admin Settings/Serial: 10 selection cycles preserve both sockets/IDs, drain hidden output and block terminal input; exact eight-field/256-byte schema, explicit refresh, error/timeout containment, late cancellation/restore/identity, and concurrent serial reconnect without superseding admission.
- 8D.9 typed Serial actions: draft validation, Reset-only confirmation, bounded JSON/CSRF, explicit working/persisted effects, automatic bounded result checks, failure/timeout containment, session fencing and preserved sockets/writer identity.
- Lost-acknowledgement and replaced-result uncertainty survives repeated result checks, failed reads, refresh and navigation; a newly acknowledged explicit submission starts a new result context.
- Immediate completion on the first GET, pending then completion, 10-attempt exhaustion and manual recovery, 15-second overall abort during fetch/body reads, delayed timers/replies, automatic read errors, visible stale snapshots during refresh and after refresh failure for every terminal outcome, late refresh cancellation, no routine confirmations and Reset cancellation. Navigation, pagehide/restore, logout, expiry and changed identity cancel checks without automatic resumption.
- Repeated current Settings selection is a no-op during submission, between and during result checks, and during completion refresh: requests, timers, visible values/control state, final outcome and socket/writer identity remain intact.
- Accounts: admin-only strict eight-user/1,024-byte list, no secret/key fields, confirmed identity-bound role/delete, automatic completion/list refresh, ten-check limit/manual recovery, stale/protected/failure and lost-acknowledgement handling, cancellation, and 401/identity isolation. The original 41 groups remain; the former self-denial assertion now checks enabled self actions and confirmation cancellation.
- Second slice: exact create/password JSON and CSRF, 768-byte request ceiling, untrimmed 12–64 printable ASCII passwords including spaces/quotes/backslashes, confirmation and username validation, separate bodyless generation without mutation/list changes, strict 24-character base64url/96-byte generation response.
- Generated acknowledgement binds value and operation/target identity; edits, regeneration, target/purpose changes reset it. 60-second lifetime, including delayed timer admission checks; submission/cancel/failure and all lifecycle wipes. Late headers/streamed bodies, concurrent reconnect and newer snapshots are fenced.
- Self password/role/delete warnings and POST/poll 401 close both routes without success claims or proactive logout. Safe duplicate/full messages and no routine secret outputs, storage, clipboard writes or history APIs. 57 Node groups total, plus renderer/HTML/CSP checks, reported PASS by the UI continuation agent (four added beyond its earlier 53-group slice 2 run).
Automatic result-check budget
After a valid POST acknowledgement, the UI waits 1,000 ms before the first result GET and between completed pending-result checks. It makes at most 10 GET attempts and uses an independent 15,000 ms overall deadline, measured with the monotonic browser clock from acknowledgement. Each attempt first revalidates the session; that time is included in the deadline. There is only one automatic check in flight. Delayed timer callbacks and replies also check this deadline. Expiry actively aborts the in-flight request and releases the UI for manual recovery; late completions cannot update the view.
The first limit reached stops automatic checking. A read error also stops it. The budget does not cancel backend work and is not a server execution deadline. POST is never automatically retried. Lost acknowledgement requires explicit Check Result recovery; manual checks do not restart automatic polling. Exhausted or cancelled polling never resumes on navigation or bfcache restoration.
Every known terminal result, including failure/cancellation, triggers one working snapshot refresh while retaining the operation outcome and any uncertainty warning. Snapshot refresh is outside the auto-check budget and retains the existing 15-second per-request bound (session validation and snapshot GET are separate requests). Settings remain visible but conflicting controls are disabled during work; old snapshots are explicitly stale during pending/uncertain work or a failed refresh. A successful refresh replaces the browser draft. Only Reset asks for confirmation among Serial actions, specifically because it overwrites saved configuration. Every Accounts mutation retains an explicit confirmation.
Tests use a deterministic clock and individually fired timer callbacks, including callbacks invoked after cancellation and fetch/body doubles that ignore abort. These deliberately exercise fences beyond normal browser cancellation behavior.
Integration and known gaps
This covers 8D.3 session behavior, the 8D.6 selector, 8D.8 Settings and the 8D.9
Serial UI and both 8D.10 Accounts slices. Operation/generation responses are fetch
doubles, not end-to-end execution of web_serial_settings.c,
web_account_settings.c, dispatcher work, credential generation/derivation,
serial reconfiguration or NVS persistence. The renderer
still relies on its caller to authenticate resources; protected asset failures
must be 401, never a redirect to HTML served as JavaScript. No Basic fallback is
implemented here. Existing 8D.5 server authorization/protocols are unchanged.
These tests model DOM, timers, fetch cancellation and WebSocket events. They do not prove real-browser CSP enforcement, script-loading errors, TLS/HTTPD behavior, actual bfcache policy, cookie expiry, server revocation, or hardware serial byte integrity, actual xterm escape parsing, hidden prompts, or desktop/mobile layout. See Accounts contracts and acceptance evidence. The following are historical 8D.10 slice 2 reports, not current totals: parent build PASS 25.61 s, 95,908 B RAM / 1,694,237 B flash. The generated endpoint is independently optionally registered (23 handlers), with route-agent lifecycle 15 PASS for failure isolation/restart. UI 57/CSP and lifecycle 15 results are agent-attributed, not claims of the parent's additional reruns. The former pending target-signoff gate is superseded by the 8D.22 acceptance; timer runtime measurements remain unreported. No new sanitizer/device execution or target resource reserve is claimed. Browser secret references are dropped and never persisted/logged, but JavaScript cannot securely wipe engine-managed strings.