agent-zero/tests/test_webui_message_ordering_static.py
frdel bebe6826cf
Some checks failed
Build And Publish Docker Images / plan (push) Has been cancelled
Build And Publish Docker Images / build (push) Has been cancelled
Improve WebUI bundling, manifest and stop command
Introduce a WebUI extension manifest and rewrite the asset bundler/server to support caller-supplied entry sets, extension-injected entries, and negotiated gzip. Key changes: add get_webui_extension_manifest() to helpers/extension, refactor Stop logic into stop_context() and reuse it from the connector `/stop` command, and add a new `/rename` slash command for chat naming. ui_bundler now accepts entry_urls, includes enabled extension entry files, raises the embedded text file size limit to 512 KiB, computes per-entry-set cache keys, and returns a bundle version based on the bundle signature. ui_server applies Starlette GZip middleware, adds routes (/, /index.html, /ui/index, /safe), serves splash/safe documents, injects the serialized webui_extension_manifest into the rendered index, and streamlines the /ui/asset-bundle endpoint with ETag and gzip handling. Also add multiple WebUI assets and fonts, new/updated plugin command YAML and Python command handlers, and corresponding tests covering bundling, commands, chat naming, and WebUI behaviors. Documentation (.dox.md) updated to reflect the new runtime contracts and guidance.
2026-07-29 19:49:07 +02:00

137 lines
5.3 KiB
Python

import re
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[1]
def read(*parts: str) -> str:
return (PROJECT_ROOT / Path(*parts)).read_text(encoding="utf-8")
def test_full_log_replays_replace_existing_message_dom():
index_js = read("webui", "index.js")
messages_js = read("webui", "js", "messages.js")
assert "snapshot.logs?.[0]?.no === 0" in index_js
assert "msgs.resetMessageRenderState();" in index_js
assert "export function resetMessageRenderState" in messages_js
assert "normalized.sort(" in messages_js
def test_message_ordering_uses_a_bounded_tail_first_renderer_cache():
messages_js = read("webui", "js", "messages.js")
message_window_js = read("webui", "js", "message-window.js")
assert 'from "./message-window.js"' in messages_js
assert "_messageWindow.compactTailIfNeeded()" in messages_js
assert "_messageWindow.visibleMessages()" in messages_js
assert "class MessageWindow" in message_window_js
assert "showTail()" in message_window_js
assert "shiftOlder()" in message_window_js
assert "shiftNewer()" in message_window_js
assert "_messageWindowFollowTail" in messages_js
assert "_messageWindow.showTail()" in messages_js
def test_virtual_paging_uses_passive_loaders_and_cancels_stale_scrolling():
messages_js = read("webui", "js", "messages.js")
scroller_js = read("webui", "js", "scroller.js")
loading_css = read("webui", "css", "loading-indicators.css")
assert "createMessageWindowIndicator" in messages_js
assert 'indicator.setAttribute("role", "status")' in messages_js
assert "createThreeBubbleLoader({ active: isLoading })" in messages_js
assert "@keyframes three-bubble-loader-jump" in loading_css
assert "Load ${Math.min" not in messages_js
assert "export function cancelPendingScroll" in scroller_js
assert "cancelPendingScroll(history)" in messages_js
def test_context_switch_uses_three_bubble_chat_loading_splash():
index_html = read("webui", "index.html")
index_js = read("webui", "index.js")
messages_js = read("webui", "js", "messages.js")
messages_css = read("webui", "css", "messages.css")
loading_js = read("webui", "js", "loading-indicators.js")
loading_css = read("webui", "css", "loading-indicators.css")
welcome_html = read("webui", "components", "welcome", "welcome-screen.html")
splash = re.search(
r'<div id="chat-loading-splash"(?P<body>.*?)</div>',
index_html,
flags=re.DOTALL,
)
assert splash
assert "for (let index = 0; index < 3; index += 1)" in loading_js
assert "createThreeBubbleLoader({ active: true })" in index_js
assert "createThreeBubbleLoader({ active: isLoading })" in messages_js
assert "beginChatLoading(id)" in index_js
assert "id !== loadingContext" in index_js
assert "finishChatLoading(snapshot.context)" in index_js
assert "CHAT_LOADING_TEST_MIN_DURATION_MS" not in index_js
assert "const CHAT_LOADING_SPLASH_DELAY_MS = 300" in index_js
assert "chatLoadingSplashVisible = true" in index_js
assert "@keyframes chat-loading-splash-fade-in" in messages_css
index_css = read("webui", "index.css")
assert "'chat-active': $store.welcomeStore" in index_html
assert "#right-panel.chat-active" in index_css
assert "background-color 0.2s ease-out" in index_css
assert "background: var(--color-background)" not in welcome_html
assert "--three-bubble-loader-delay: 0.16s" in loading_css
assert "--three-bubble-loader-delay: 0.32s" in loading_css
assert "animation-delay: var(--three-bubble-loader-delay)" in loading_css
def test_utility_prefixed_process_groups_are_not_hidden_from_partial_dom_state():
messages_js = read("webui", "js", "messages.js")
preferences_js = read(
"webui",
"components",
"sidebar",
"bottom",
"preferences",
"preferences-store.js",
)
process_group_css = read(
"webui",
"components",
"messages",
"process-group",
"process-group.css",
)
assert 'else if (log.type === "util")' in messages_js
assert 'group.classList.remove("utility-only")' in messages_js
assert "isUtilityOnlyProcessGroup" not in messages_js
assert "group.hidden" not in messages_js
assert '"show-utility-messages"' in preferences_js
assert ".show-utility-messages .process-group.utility-only" in process_group_css
assert ".process-step.message-util {" in process_group_css
assert ".show-utility-messages .process-step.message-util" in process_group_css
def test_message_actions_put_copy_before_speak():
sources = [
read("webui", "js", "messages.js"),
read(
"plugins",
"_browser",
"extensions",
"webui",
"get_tool_message_handler",
"browser-tool-handler.js",
),
]
for source in sources:
lines = source.splitlines()
for index, line in enumerate(lines):
if 'createActionButton("speak"' not in line:
continue
preceding_actions = [
match.group(1)
for candidate in lines[max(0, index - 12) : index]
if (match := re.search(r'createActionButton\("(detail|copy|speak)"', candidate))
]
assert preceding_actions and preceding_actions[-1] == "copy"