"""Unit: the phase-39 brand layer (BOR_APP_NAME → window.BOR_BRAND). No Python logic exists for this task — the behavior lives in ``frontend/assets/brand.js`` + the templates + the page scripts, and it is E2E-gated by the story suite (task 03). Like the other frontend-adjacent unit files, this module pins the JS markers the story depends on, so a silent regression in the brand layer is caught without a browser. """ from __future__ import annotations import re from pathlib import Path FRONTEND = Path(__file__).resolve().parents[2] / "frontend" ROOT = FRONTEND.parent BRAND_JS = FRONTEND / "assets" / "brand.js" APP_JS = FRONTEND / "assets" / "app.js" DOCUMENT_JS = FRONTEND / "assets" / "document.js" CONTAINERFILE = ROOT / "Containerfile" #: Every page in the app ships the brand layer (phase 39: "every visible #: brand string on every page resolves from one place"). HTML_PAGES = ( "index.html", "sources.html", "tuning.html", "document.html", "login.html", "git-sources.html", ) def _text(path: Path) -> str: return path.read_text(encoding="utf-8") def test_brand_js_is_a_classic_script_with_synchronous_default() -> None: """brand.js is NOT a module (its top level must run at parse time, before the page's module scripts evaluate) and sets window.BOR_BRAND synchronously, BEFORE the /api/config fetch starts.""" js = _text(BRAND_JS) assert not re.search(r"^\s*import\s", js, re.M), ( "brand.js must be a classic script — no import statements" ) assert not re.search(r"^\s*export\s", js, re.M), ( "brand.js must be a classic script — no export statements" ) default_idx = js.find('window.BOR_BRAND = "Brain of Reese"') fetch_idx = js.find('fetch("/api/config"') assert 0 <= default_idx < fetch_idx, ( "the default name must be set at top level before the fetch" ) def test_brand_js_fetches_api_config_no_store() -> None: """The single source of the name is GET /api/config, never cached — a renamed app must not serve a stale name from the HTTP cache — and a fetch failure only warns (the loadHealth house style: the page never breaks).""" js = _text(BRAND_JS) assert 'fetch("/api/config", { cache: "no-store" })' in js assert "console.warn" in js def test_brand_js_defers_dom_application_until_ready() -> None: """The DOM passes run on DOMContentLoaded while parsing, otherwise immediately (the script sits at the end of ).""" js = _text(BRAND_JS) assert 'document.readyState === "loading"' in js assert 'document.addEventListener("DOMContentLoaded", applyBrand)' in js def test_brand_js_reskins_title_brand_text_prose_and_attributes() -> None: """The four DOM passes (phase 39 task 02): the document title, every .brand-text (bold split for "Brain of …" names, plain text otherwise — the name is HTML-escaped before innerHTML), a TreeWalker over the text nodes (script/style nodes rejected — the page source is never rewritten), and the aria-label / placeholder / meta content attributes.""" js = _text(BRAND_JS) assert "document.title.replaceAll" in js assert ".brand-text" in js assert 'name.startsWith("Brain of ")' in js assert "escapeHTML" in js, "the name must be escaped before innerHTML" assert "el.textContent = name" in js, "non-'Brain of ' names render plain" assert "document.createTreeWalker" in js assert "NodeFilter.SHOW_TEXT" in js assert 'tag === "SCRIPT" || tag === "STYLE"' in js assert "replaceAll(BRAND_LITERAL, name)" in js for marker in ('"aria-label"', '"placeholder"', "meta[content]"): assert marker in js, f"the attribute pass must cover {marker}" def test_page_scripts_keep_the_default_literal_exactly_once() -> None: """The fallback literal lives in the page scripts' brand() reads — exactly one copy per file (a second copy could drift out of sync).""" assert _text(APP_JS).count('"Brain of Reese"') == 1, ( "app.js: the literal must appear only in the brand() fallback" ) assert _text(DOCUMENT_JS).count('"Brain of Reese"') == 1, ( "document.js: the literal must appear only in the brand() fallback" ) def test_app_js_labels_resolve_the_name_at_call_time() -> None: """Phase 39 task 02: the status labels, the typing label, the elapsed-seconds hint and the tool labels read window.BOR_BRAND through brand() — at CALL time, so a label set after /api/config lands carries the configured name (a module-level const would freeze the default).""" js = _text(APP_JS) assert 'const brand = () => window.BOR_BRAND || "Brain of Reese";' in js assert "`${brand()} is thinking`" in js assert "`${brand()} is answering`" in js assert "`${brand()} is still thinking (${secs}s)`" in js assert "`${brand()} is reading ${argument}`" in js assert "`${brand()} is listing documents`" in js # The frozen module-level literals must be gone (stale-name bug). assert '"Brain of Reese is thinking"' not in js assert '"Brain of Reese is answering"' not in js def test_document_js_titles_resolve_the_name() -> None: """The viewer page titles (render + not-found) carry the resolved name — the module sets them itself, so it must use brand() (the static document.html title is handled by the brand.js title pass).""" js = _text(DOCUMENT_JS) assert 'document.title = `${doc.title} · ${brand()}`' in js assert 'document.title = `Document not found · ${brand()}`' in js assert 'window.BOR_BRAND || "Brain of Reese"' in js def test_every_page_loads_brand_js_before_its_module_script() -> None: """All pages load brand.js as a CLASSIC script, before the page's module script (modules execute after parsing — the synchronous default must be set first).""" for page in HTML_PAGES: html = _text(FRONTEND / page) brand_idx = html.find('') module_idx = html.find('