116 lines
4.7 KiB
Python
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"
|