Hermes WebUI v0.1.0 — initial public release
This commit is contained in:
115
tests/test_sprint9.py
Normal file
115
tests/test_sprint9.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Sprint 9 Tests: app.js module split verification, tool cards, todo panel.
|
||||
Run: python -m pytest tests/test_sprint9.py -v
|
||||
"""
|
||||
import json, pathlib, urllib.error, urllib.request
|
||||
|
||||
BASE = "http://127.0.0.1:8788"
|
||||
|
||||
def get_text(path):
|
||||
with urllib.request.urlopen(BASE + path, timeout=10) as r:
|
||||
return r.read().decode()
|
||||
|
||||
def get(path):
|
||||
with urllib.request.urlopen(BASE + path, timeout=10) as r:
|
||||
return json.loads(r.read())
|
||||
|
||||
def post(path, body=None):
|
||||
data = json.dumps(body or {}).encode()
|
||||
req = urllib.request.Request(BASE + path, data=data,
|
||||
headers={"Content-Type": "application/json"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as r:
|
||||
return json.loads(r.read()), r.status
|
||||
except urllib.error.HTTPError as e:
|
||||
return json.loads(e.read()), e.code
|
||||
|
||||
# ── Module split: all 6 files served ──────────────────────────────────────
|
||||
|
||||
def test_ui_js_served(cleanup_test_sessions):
|
||||
src = get_text("/static/ui.js")
|
||||
assert len(src) > 1000
|
||||
assert "function setBusy" in src
|
||||
assert "function syncTopbar" in src
|
||||
assert "const S=" in src or "const S =" in src
|
||||
|
||||
def test_workspace_js_served(cleanup_test_sessions):
|
||||
src = get_text("/static/workspace.js")
|
||||
assert "async function api(" in src
|
||||
assert "async function loadDir(" in src
|
||||
assert "async function openFile(" in src # renderFileTree is in ui.js
|
||||
|
||||
def test_sessions_js_served(cleanup_test_sessions):
|
||||
src = get_text("/static/sessions.js")
|
||||
assert "async function newSession(" in src
|
||||
assert "async function loadSession(" in src
|
||||
assert "async function renderSessionList(" in src
|
||||
|
||||
def test_messages_js_served(cleanup_test_sessions):
|
||||
src = get_text("/static/messages.js")
|
||||
assert "async function send(" in src
|
||||
assert "function transcript(" in src
|
||||
|
||||
def test_panels_js_served(cleanup_test_sessions):
|
||||
src = get_text("/static/panels.js")
|
||||
assert "async function switchPanel(" in src
|
||||
assert "async function loadCrons(" in src
|
||||
assert "async function loadSkills(" in src
|
||||
assert "async function loadMemory(" in src
|
||||
|
||||
def test_boot_js_served(cleanup_test_sessions):
|
||||
src = get_text("/static/boot.js")
|
||||
assert "btnSend" in src
|
||||
assert "btnNewChat" in src
|
||||
# boot IIFE
|
||||
assert "(async()=>{" in src or "(async () => {" in src
|
||||
|
||||
def test_app_js_no_longer_referenced_in_html(cleanup_test_sessions):
|
||||
"""index.html must not reference the old monolithic app.js."""
|
||||
html = get_text("/")
|
||||
assert 'src="/static/app.js"' not in html
|
||||
# All 6 modules must be present
|
||||
for module in ["ui.js", "workspace.js", "sessions.js", "messages.js", "panels.js", "boot.js"]:
|
||||
assert f'src="/static/{module}"' in html, f"Missing {module} in index.html"
|
||||
|
||||
def test_module_load_order_correct(cleanup_test_sessions):
|
||||
"""ui.js must appear before sessions.js which must appear before boot.js."""
|
||||
html = get_text("/")
|
||||
ui_pos = html.find('src="/static/ui.js"')
|
||||
ws_pos = html.find('src="/static/workspace.js"')
|
||||
sess_pos = html.find('src="/static/sessions.js"')
|
||||
msg_pos = html.find('src="/static/messages.js"')
|
||||
panels_pos = html.find('src="/static/panels.js"')
|
||||
boot_pos = html.find('src="/static/boot.js"')
|
||||
assert ui_pos < ws_pos < sess_pos < msg_pos < panels_pos < boot_pos
|
||||
|
||||
def test_no_duplicate_function_definitions(cleanup_test_sessions):
|
||||
"""No function name should appear in more than one module."""
|
||||
import re
|
||||
modules = ["ui.js", "workspace.js", "sessions.js", "messages.js", "panels.js", "boot.js"]
|
||||
seen = {}
|
||||
for m in modules:
|
||||
src = get_text(f"/static/{m}")
|
||||
fns = re.findall(r'(?:async )?function ([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(', src)
|
||||
for fn in fns:
|
||||
if fn in seen:
|
||||
assert False, f"Duplicate function {fn} in both {seen[fn]} and {m}"
|
||||
seen[fn] = m
|
||||
assert len(seen) > 50, f"Expected 50+ functions, got {len(seen)}"
|
||||
|
||||
def test_all_functions_present_across_modules(cleanup_test_sessions):
|
||||
"""Key functions must be present somewhere in the split modules."""
|
||||
import re
|
||||
modules = ["ui.js", "workspace.js", "sessions.js", "messages.js", "panels.js", "boot.js"]
|
||||
all_src = ""
|
||||
for m in modules:
|
||||
all_src += get_text(f"/static/{m}")
|
||||
required = [
|
||||
"setBusy", "syncTopbar", "renderMessages", "send", "loadSession",
|
||||
"newSession", "renderSessionList", "loadDir", "switchPanel",
|
||||
"loadCrons", "loadSkills", "loadMemory", "editMessage",
|
||||
"regenerateResponse", "clearConversation", "highlightCode",
|
||||
"toggleSkillForm", "submitSkillSave", "toggleMemoryEdit",
|
||||
]
|
||||
for fn in required:
|
||||
assert fn in all_src, f"Function {fn} missing from all modules"
|
||||
Reference in New Issue
Block a user