fix(build): Containerfile builds again — relative module imports, all four pages and shared assets in the image
This commit is contained in:
@@ -127,22 +127,32 @@ def test_nav_sources_is_absent_from_the_viewer() -> None:
|
||||
def test_sources_and_viewer_carry_the_shared_controls() -> None:
|
||||
"""Sources AND the document viewer gain the New Chat button + the
|
||||
Sign in / Sign out pair (both starting hidden — initSharedHeader
|
||||
reveals exactly one after whoami), and they load header.js."""
|
||||
reveals exactly one after whoami), and their page scripts load
|
||||
header.js (phase 23: via the page script's relative import, not a
|
||||
direct script tag)."""
|
||||
for html in (SOURCES_HTML, DOCUMENT_HTML):
|
||||
text = _text(html)
|
||||
assert 'id="new-chat-btn"' in text
|
||||
assert re.search(r'id="sign-in-link"[^>]*\bhidden\b', text)
|
||||
assert re.search(r'id="sign-out-btn"[^>]*\bhidden\b', text)
|
||||
assert "header.js" in text, "page must load the shared header module"
|
||||
for js_file in (SOURCES_JS, DOCUMENT_JS):
|
||||
assert 'from "./header.js"' in _text(js_file), (
|
||||
f"{js_file.name}: page script must load the shared header module"
|
||||
)
|
||||
# Each page's Sign in link returns to ITS OWN page after login.
|
||||
assert 'href="/login.html?next=/sources.html"' in _text(SOURCES_HTML)
|
||||
assert 'href="/login.html?next=/document.html"' in _text(DOCUMENT_HTML)
|
||||
|
||||
|
||||
def test_header_module_loads_before_the_page_script() -> None:
|
||||
"""Every page loads header.js (type=module) BEFORE its page script,
|
||||
so the sign-out binding and the whoami cache exist when the page
|
||||
script boots."""
|
||||
"""Phase 23 (owner-confirmed single-evaluation design): NO page loads
|
||||
header.js with a direct <script> tag anymore. Each page script
|
||||
imports it relatively (`from "./header.js"`) — a hoisted import that
|
||||
the browser evaluates BEFORE the page script body runs, and that the
|
||||
image bundler inlines into the page bundle. The sign-out binding and
|
||||
the whoami cache therefore exist when the page script boots, and
|
||||
header.js can never be evaluated twice on a page (a tag + import pair
|
||||
would double-bind the sign-out listener)."""
|
||||
cases = [
|
||||
(INDEX_HTML, "app.js"),
|
||||
(SOURCES_HTML, "sources.js"),
|
||||
@@ -151,12 +161,18 @@ def test_header_module_loads_before_the_page_script() -> None:
|
||||
]
|
||||
for html, page_script in cases:
|
||||
srcs = _script_srcs(html)
|
||||
header_idx = [i for i, s in enumerate(srcs) if "header.js" in s]
|
||||
page_idx = [i for i, s in enumerate(srcs) if page_script in s]
|
||||
assert header_idx, f"{html.name}: must load header.js"
|
||||
assert page_idx, f"{html.name}: must load {page_script}"
|
||||
assert header_idx[0] < page_idx[0], (
|
||||
f"{html.name}: header.js must load before {page_script}"
|
||||
assert [s for s in srcs if "header.js" in s] == [], (
|
||||
f"{html.name}: no direct header.js <script> tag (single-evaluation design)"
|
||||
)
|
||||
assert [s for s in srcs if page_script in s], (
|
||||
f"{html.name}: must load {page_script}"
|
||||
)
|
||||
js = _text(ASSETS / page_script)
|
||||
assert 'from "./header.js"' in js, (
|
||||
f"{page_script}: must import the shared header module relatively"
|
||||
)
|
||||
assert 'from "/assets/header.js"' not in js, (
|
||||
f"{page_script}: absolute header import would break the esbuild bundle"
|
||||
)
|
||||
|
||||
|
||||
@@ -177,9 +193,10 @@ def test_app_js_delegates_the_shared_controls_to_header_module() -> None:
|
||||
"""app.js imports the shared module, runs initSharedHeader() at boot
|
||||
(BEFORE the phase-14 restore), takes its isAdmin from the cached
|
||||
fetchIsAdmin(), and owns NO whoami fetch and NO sign-out binding
|
||||
anymore (both moved to header.js)."""
|
||||
anymore (both moved to header.js). Phase 23: the import is relative
|
||||
(`./header.js`) so esbuild can bundle it into the image."""
|
||||
js = _text(APP_JS)
|
||||
assert 'from "/assets/header.js"' in js
|
||||
assert 'from "./header.js"' in js
|
||||
assert "fetchIsAdmin" in js and "initSharedHeader" in js
|
||||
assert "signOutBtn.addEventListener" not in js, (
|
||||
"the sign-out binding moved to header.js"
|
||||
@@ -200,9 +217,10 @@ def test_app_js_delegates_the_shared_controls_to_header_module() -> None:
|
||||
def test_login_js_uses_the_shared_fetch_is_admin() -> None:
|
||||
"""login.js switches its whoami check to the shared cached promise
|
||||
(one request per page) and calls initSharedHeader for the Sources
|
||||
link; its already-admin → redirect behavior is unchanged."""
|
||||
link; its already-admin → redirect behavior is unchanged. Phase 23:
|
||||
the import is relative (`./header.js`)."""
|
||||
js = _text(LOGIN_JS)
|
||||
assert 'from "/assets/header.js"' in js
|
||||
assert 'from "./header.js"' in js
|
||||
assert "fetchIsAdmin" in js
|
||||
assert "fetchIsAdmin()" in js
|
||||
assert "initSharedHeader()" in js
|
||||
@@ -214,10 +232,11 @@ def test_non_chat_pages_bind_new_chat_to_the_chat_page() -> None:
|
||||
"""On sources and the viewer, New Chat means "go to the chat,
|
||||
fresh": the binding clears the phase-14 key (clearChatStorage) and
|
||||
navigates to "/" — and both pages run initSharedHeader() at boot
|
||||
on the shared cached whoami."""
|
||||
on the shared cached whoami. Phase 23: the import is relative
|
||||
(`./header.js`)."""
|
||||
for js_file in (SOURCES_JS, DOCUMENT_JS):
|
||||
js = _text(js_file)
|
||||
assert 'from "/assets/header.js"' in js
|
||||
assert 'from "./header.js"' in js
|
||||
assert "initSharedHeader()" in js
|
||||
btn_idx = js.find("new-chat-btn")
|
||||
clear_idx = js.find("clearChatStorage();")
|
||||
|
||||
Reference in New Issue
Block a user