Files
webui-develop/tests/test_sprint9.py
2026-04-20 10:43:30 +02:00

116 lines
4.7 KiB
Python

"""
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
from tests._pytest_port import BASE
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"