various fixes

This commit is contained in:
2026-08-28 09:42:19 -04:00
parent 5d679f5184
commit 03bead092c
19 changed files with 983 additions and 1443 deletions
+4 -3
View File
@@ -219,8 +219,9 @@ def test_header_z_index_is_20_so_the_dropdown_uses_21() -> None:
def test_mobile_block_renders_the_44px_toggle() -> None:
"""At ≤640px the toggle is a 44px×44px button (the phase-07 touch
floor), ghost look like the other pills, a hover state in the
.steering-toggle:hover family, and a SIZED icon (an unsized inline
SVG defaults to 300px and would blow the bar out at 360px).
shared pill hover family (brand-ink on brand-soft, as
.nav-link:hover), and a SIZED icon (an unsized inline SVG defaults
to 300px and would blow the bar out at 360px).
:focus-visible needs no rule — the global 3px outline applies."""
mobile = _media_block(_css(), "@media (max-width: 640px)")
block = _rule_block(mobile, ".nav-toggle")
@@ -239,7 +240,7 @@ def test_mobile_block_renders_the_44px_toggle() -> None:
hover = _rule_block(mobile, ".nav-toggle:hover")
assert "background: var(--brand-soft)" in hover
assert "color: var(--brand-ink)" in hover, (
"hover matches the .steering-toggle:hover family (≈6.9:1 pair)"
"hover matches the shared pill hover family (≈6.9:1 pair)"
)
icon = _rule_block(mobile, ".nav-toggle svg")
assert "width: 20px" in icon and "height: 20px" in icon, (
+20 -21
View File
@@ -125,8 +125,9 @@ def test_nav_sources_ships_hidden_on_every_nav_page() -> None:
def test_all_five_pages_share_the_header_control_order() -> None:
"""Phase 34 task 03 (owner confirmation 2026-08-26): every page
ships the IDENTICAL header control inventory in the IDENTICAL
order — brand, nav [Chat, Sources, Tuning], #steering-toggle,
#sync-btn, #new-chat-btn, Sign in, Sign out — inside the shared
order — brand, nav [Chat, Sources, Tuning], #sync-btn, #new-chat-btn,
Sign in, Sign out (the #steering-toggle was removed from the navbar
at owner request, 2026-08-28) — inside the shared
.header-inner row (the document viewer's row 1). Only the
current-page is-active nav marker and the static ?next= fallback
may differ per page (task 05's story E2E pins the rendered
@@ -137,7 +138,6 @@ def test_all_five_pages_share_the_header_control_order() -> None:
'href="/"',
'id="nav-sources"',
'id="nav-tuning"',
'id="steering-toggle"',
'id="sync-btn"',
'id="new-chat-btn"',
'id="sign-in-link"',
@@ -288,16 +288,16 @@ def test_login_page_carries_the_full_header() -> None:
"""Phase 34 task 03 (owner confirmation 2026-08-26): the noted
boundary is reversed by owner decision — the login page finally
carries the FULL shared header: the nav gains the #nav-tuning link
(same ship-hidden markup as the other pages), plus the Tuning
toggle, the admin-only Sync button, New chat, and the Sign in /
Sign out pair (ship hidden — initSharedHeader reveals exactly one
after whoami; the static ?next= fallback is the login page itself).
(same ship-hidden markup as the other pages), plus the admin-only
Sync button, New chat, and the Sign in / Sign out pair (ship hidden
— initSharedHeader reveals exactly one after whoami; the static
?next= fallback is the login page itself). The Tuning toggle is NOT
among them — removed from the navbar at owner request (2026-08-28).
No nav link is "current" on the auth page."""
text = _text(LOGIN_HTML)
for marker in (
'id="nav-sources"',
'id="nav-tuning"',
'id="steering-toggle"',
'id="sync-btn"',
'id="new-chat-btn"',
'id="sign-in-link"',
@@ -410,12 +410,11 @@ def test_header_module_owns_the_steering_panel() -> None:
"""header.js owns the steering panel behavior (moved from app.js in
phase 34 task 01): the module-level null-safe element refs, the
newest-first textContent render (XSS contract), the labeled
per-note delete, the count badge, the announcer, and the toggle
binding that runs at module import (like the sign-out binding)."""
per-note delete, and the announcer — and NO toggle wiring anymore
(the navbar #steering-toggle was removed at owner request,
2026-08-28; note management lives on /tuning.html)."""
js = _text(HEADER_JS)
for selector in (
"#steering-toggle",
"#steering-count",
"#steering-panel",
"#steering-list",
"#steering-empty",
@@ -430,9 +429,8 @@ def test_header_module_owns_the_steering_panel() -> None:
assert 'del.setAttribute("aria-label", `Delete tuning note: ${n.note}`)' in js
assert "async function deleteSteeringNote" in js
assert 'fetch(`/api/steering/${encodeURIComponent(id)}`, { method: "DELETE" })' in js
assert "steeringToggle.addEventListener" in js, "the toggle binding is module-owned"
assert 'setAttribute("aria-expanded"' in js
assert "refreshSteering()" in js # re-open refreshes the list
assert "refreshSteering()" in js # the admin boot refresh + the form's save
assert "steeringToggle" not in js, "the navbar toggle is gone (2026-08-28)"
def test_header_module_exports_refresh_and_announce_steering() -> None:
@@ -455,17 +453,18 @@ def test_header_module_exports_refresh_and_announce_steering() -> None:
def test_init_shared_header_gates_the_steering_surface() -> None:
"""Inside initSharedHeader: admin → the list refreshes (count badge
right before the panel is ever opened; only when the page ships the
panel markup); anonymous → the toggle + panel are REMOVED from the
DOM (phase-16 'absent, not hidden') and /api/steering is never
fetched."""
"""Inside initSharedHeader: admin → the list refreshes (the panel
ships hidden and is only kept fresh; only when the page ships the
panel markup); anonymous → the panel is REMOVED from the DOM
(phase-16 'absent, not hidden') and /api/steering is never fetched.
The navbar toggle was removed at owner request (2026-08-28) — no
unhide / remove of a toggle may be left behind."""
js = _text(HEADER_JS)
fn = js.find("function initSharedHeader")
assert fn != -1
body = js[fn : js.find("\n}", fn)]
assert "if (steeringPanel) refreshSteering();" in body
assert "steeringToggle?.remove();" in body
assert "steeringToggle" not in body
assert "steeringPanel?.remove();" in body
+126
View File
@@ -0,0 +1,126 @@
"""Unit: the navbar steering toggle is gone (owner request, 2026-08-28).
The "Tuning" steering toggle (``#steering-toggle``) used to sit in
every page's shared header — shipped hidden (phase 40), revealed for
the admin, removed from the DOM for anonymous. The owner asked for the
button to go away entirely: note management now lives on the
standalone Tuning page (``/tuning.html``, phase 27). This file pins the
removal at source level — the toggle + count badge are ABSENT from all
six pages, the ``#steering-panel`` section still ships hidden (kept
fresh by the chat page's per-bubble Tune form through header.js), the
shared module owns no toggle wiring anymore, and the admin-only
``#nav-tuning`` link — the surviving path to the notes — still ships
hidden.
"""
from __future__ import annotations
import re
from pathlib import Path
FRONTEND = Path(__file__).resolve().parents[2] / "frontend"
ASSETS = FRONTEND / "assets"
HEADER_JS = ASSETS / "header.js"
#: All six pages carry the shared header block (phase 34's five pages +
#: phase 35's git-sources page).
PAGES = (
FRONTEND / "index.html",
FRONTEND / "sources.html",
FRONTEND / "document.html",
FRONTEND / "git-sources.html",
FRONTEND / "login.html",
FRONTEND / "tuning.html",
)
def _text(path: Path) -> str:
assert path.is_file(), f"missing frontend file: {path}"
return path.read_text(encoding="utf-8")
def _init_body(js: str) -> str:
"""The source of initSharedHeader in header.js."""
fn = js.find("function initSharedHeader")
assert fn != -1, "initSharedHeader must be defined"
return js[fn : js.find("\n}", fn)]
# ---------- the removal: absent from every page's navbar ----------
def test_steering_toggle_removed_from_all_six_pages() -> None:
"""The #steering-toggle button (and its #steering-count badge) is
gone from the navbar of EVERY page — absent, not hidden."""
for html in PAGES:
text = _text(html)
assert 'id="steering-toggle"' not in text, (
f"{html.name}: #steering-toggle must be removed from the navbar"
)
assert 'class="steering-toggle"' not in text, (
f"{html.name}: the .steering-toggle button must be removed"
)
assert 'id="steering-count"' not in text, (
f"{html.name}: the #steering-count badge lived inside the toggle"
)
def test_steering_panel_still_ships_hidden_on_all_six_pages() -> None:
"""The #steering-panel section survives the toggle removal (the chat
page's per-bubble Tune form keeps it fresh through header.js) and
still ships hidden, with its list / empty state / announcer."""
for html in PAGES:
text = _text(html)
tag = re.search(r'<section[^>]*id="steering-panel"[^>]*>', text)
assert tag, f"{html.name}: missing the #steering-panel section"
assert re.search(r"\bhidden\b", tag.group(0)), "the panel ships hidden"
assert 'id="steering-list"' in text, f"{html.name}: missing #steering-list"
assert 'id="steering-empty"' in text, f"{html.name}: missing #steering-empty"
assert re.search(r'<p[^>]*id="steering-announcer"[^>]*role="status"[^>]*>', text), (
f"{html.name}: missing the #steering-announcer live region"
)
# ---------- header.js: no toggle wiring left, panel contract intact ----------
def test_header_js_carries_no_toggle_wiring() -> None:
"""header.js owns no #steering-toggle reference anymore: no element
lookup, no admin unhide, no anonymous removal, no click binding, no
open/close helper, no count badge."""
js = _text(HEADER_JS)
assert 'querySelector("#steering-toggle")' not in js
assert "steeringToggle" not in js
assert "setSteeringPanel" not in js
assert 'querySelector("#steering-count")' not in js
assert "steeringCount" not in js
def test_header_js_keeps_the_panel_contract() -> None:
"""What survives: inside initSharedHeader the admin branch still
refreshes the panel list (fire-and-forget) and the anonymous branch
still REMOVES the panel from the DOM (phase-16 "absent, not hidden")
so /api/steering is never fetched for anonymous visitors."""
body = _init_body(_text(HEADER_JS))
admin = body.find("if (admin)")
refresh = body.find("if (steeringPanel) refreshSteering();")
assert admin != -1 and refresh != -1, "the admin refresh is missing"
assert admin < refresh, "the refresh must live in the admin branch"
assert "steeringPanel?.remove();" in body, (
"the anonymous remove-from-DOM path must stay intact"
)
# ---------- the surviving path to the notes ----------
def test_nav_tuning_still_ships_hidden_on_all_six_pages() -> None:
"""The admin-only Tuning NAV LINK (#nav-tuning) — the surviving path
to the steering notes now that the navbar toggle is gone — still
ships hidden on every page (the phase-19/29/35 contract)."""
for html in PAGES:
tag = re.search(r'<a[^>]*id="nav-tuning"[^>]*>', _text(html))
assert tag, f"{html.name}: missing the #nav-tuning nav link"
assert re.search(r"\bhidden\b", tag.group(0)), (
f"{html.name}: #nav-tuning must ship hidden"
)
@@ -1,150 +0,0 @@
"""Unit: the tuning toggle ships hidden — the anonymous flash fix (phase 40).
The "Tuning" steering toggle (``#steering-toggle``) used to ship VISIBLE
in every page's shared header and was removed by ``assets/header.js``
only after ``/api/whoami`` resolved — so an anonymous user briefly saw
the button on every page load (``TODO.md`` L3). The fix mirrors the
admin-only NAV LINKS (phase 19/29/35 contract): the toggle now SHIPS
with the ``hidden`` attribute in all six pages and ``initSharedHeader``
unhides it only when whoami says admin. The browser behavior is
E2E-covered (``tests/e2e/test_tuning_toggle_flash.py``); here we pin
the source-level wiring — the ship-hidden markup on every page, the
admin unhide line inside ``initSharedHeader``, the intact anonymous
remove-from-DOM path (phase 16 "absent, not hidden"), and the
``#nav-tuning`` hidden contract this phase relies on — so a silent
regression is caught without a browser.
"""
from __future__ import annotations
import re
from pathlib import Path
FRONTEND = Path(__file__).resolve().parents[2] / "frontend"
ASSETS = FRONTEND / "assets"
HEADER_JS = ASSETS / "header.js"
#: All six pages carry the shared header block (phase 34's five pages +
#: phase 35's git-sources page).
PAGES = (
FRONTEND / "index.html",
FRONTEND / "sources.html",
FRONTEND / "document.html",
FRONTEND / "git-sources.html",
FRONTEND / "login.html",
FRONTEND / "tuning.html",
)
def _text(path: Path) -> str:
assert path.is_file(), f"missing frontend file: {path}"
return path.read_text(encoding="utf-8")
def _toggle_tag(html: Path) -> str:
tag = re.search(r"<button[^>]*id=\"steering-toggle\"[^>]*>", _text(html))
assert tag, f"{html.name}: missing the #steering-toggle button"
return tag.group(0)
def _toggle_body(html: Path) -> str:
"""The full <button>…</button> block, for the icon/label/badge pins."""
text = _text(html)
start = text.find('id="steering-toggle"')
assert start != -1, f"{html.name}: missing the #steering-toggle button"
return text[start : text.find("</button>", start)]
def _init_body(js: str) -> str:
"""The source of initSharedHeader in header.js."""
fn = js.find("function initSharedHeader")
assert fn != -1, "initSharedHeader must be defined"
return js[fn : js.find("\n}", fn)]
# ---------- ship-hidden markup: zero flash for anonymous ----------
def test_steering_toggle_ships_hidden_on_all_six_pages() -> None:
"""#steering-toggle carries the ``hidden`` attribute in ALL SIX
pages — the exact ship-hidden contract the admin-only nav links
use, so an anonymous user never sees the "Tuning" button for a
single frame, on any page."""
for html in PAGES:
tag = _toggle_tag(html)
assert re.search(r"\bhidden\b", tag), (
f"{html.name}: #steering-toggle must ship hidden"
)
def test_steering_toggle_keeps_its_existing_markup() -> None:
"""Only the ``hidden`` attribute was added: type, class, the
aria-expanded / aria-controls wiring, the decorative icon, the
"Tuning" label, and the #steering-count badge stay byte-identical
on every page — the admin UX is unchanged."""
for html in PAGES:
tag = _toggle_tag(html)
assert 'type="button"' in tag
assert 'class="steering-toggle"' in tag
assert 'aria-expanded="false"' in tag
assert 'aria-controls="steering-panel"' in tag
body = _toggle_body(html)
assert '<svg aria-hidden="true"' in body, f"{html.name}: icon is gone"
assert '<span class="steering-label">Tuning</span>' in body, (
f"{html.name}: label markup changed"
)
assert '<span class="steering-count" id="steering-count">0</span>' in body, (
f"{html.name}: count badge markup changed"
)
def test_steering_panel_still_ships_hidden_on_all_six_pages() -> None:
"""The #steering-panel section already shipped hidden and stays
that way (this phase never touches the panel)."""
for html in PAGES:
tag = re.search(r'<section[^>]*id="steering-panel"[^>]*>', _text(html))
assert tag, f"{html.name}: missing the #steering-panel section"
assert re.search(r"\bhidden\b", tag.group(0)), "the panel ships hidden"
# ---------- header.js: reveal-for-admin, anonymous removal intact ----------
def test_header_js_unhides_the_toggle_for_admin() -> None:
"""Inside initSharedHeader, the admin branch unhides the toggle —
and that line sits BEFORE the refreshSteering() call (the count
badge is right before the panel is ever opened)."""
body = _init_body(_text(HEADER_JS))
assert "if (steeringToggle) steeringToggle.hidden = false;" in body, (
"the admin unhide is missing from initSharedHeader"
)
admin = body.find("if (admin)")
unhide = body.find("steeringToggle.hidden = false")
refresh = body.find("if (steeringPanel) refreshSteering();")
assert -1 < admin < unhide, "the unhide must live in the admin branch"
assert unhide < refresh, "the unhide must precede the refreshSteering() call"
def test_anonymous_removal_path_is_intact() -> None:
"""The phase-16 "absent, not hidden" contract is preserved: the
anonymous branch still REMOVES the toggle + panel from the DOM —
the new hidden attribute only closes the pre-whoami flash window,
the end state (absent) is unchanged."""
body = _init_body(_text(HEADER_JS))
assert "steeringToggle?.remove();" in body
assert "steeringPanel?.remove();" in body
# ---------- the nav contract this phase relies on ----------
def test_nav_tuning_still_ships_hidden_on_all_six_pages() -> None:
"""The admin-only Tuning NAV LINK (#nav-tuning) — the contract the
toggle now mirrors — still ships hidden on every page: one
ship-hidden / reveal-for-admin family, nav link and toggle alike."""
for html in PAGES:
tag = re.search(r'<a[^>]*id="nav-tuning"[^>]*>', _text(html))
assert tag, f"{html.name}: missing the #nav-tuning nav link"
assert re.search(r"\bhidden\b", tag.group(0)), (
f"{html.name}: #nav-tuning must ship hidden"
)