test_chat_mobile.py 4.53 KB
#!/usr/bin/env python3
"""RED phase test: Verify that /chat page correctly extends base.html and has
responsive layout + session loading.

Before fix (RED): Page is standalone with no topbar/☰ menu → FAIL.
After fix (GREEN): Page extends base.html with topbar, ☰, session sidebar toggle → PASS.
"""

import subprocess, sys, json

BASE_URL = "http://127.0.0.1:5000"
FAILURES = []

def check(name, condition):
    if condition:
        print(f"  [PASS] {name}")
    else:
        print(f"  [FAIL] {name}")
        FAILURES.append(name)

def curl(url):
    r = subprocess.run(["curl", "-s", url, "--connect-timeout", "5", "--max-time", "10"],
                       capture_output=True, text=True, timeout=15)
    return r.stdout, r.returncode

def curl_status(url):
    r = subprocess.run(["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",
                        url, "--connect-timeout", "5", "--max-time", "10"],
                       capture_output=True, text=True, timeout=15)
    return r.stdout.strip()

def curl_json(url):
    html, _ = curl(url)
    try:
        return json.loads(html)
    except:
        return None

# ── Test Suite ──

print("=== TC0: Page accessibility ===")
status = curl_status(f"{BASE_URL}/chat")
check("GET /chat returns 200", status == "200")

html, _ = curl(f"{BASE_URL}/chat")

print("\n=== TC1: base.html integration ===")
# After fix, /chat should extend base.html and have these:
check("Has topbar with ☰ menu button", 'class="menu-btn"' in html and '☰' in html)
check("Has drawer overlay", 'class="overlay"' in html)
check("Has drawer div", 'class="drawer"' in html)
check("Has drawer navigation", 'class="drawer-nav"' in html)
check("Has topbar brand", 'class="topbar"' in html)
check("Has main content area", 'class="main"' in html)

print("\n=== TC2: Chat-specific layout ===")
# Session sidebar + chat area
check("Has session sidebar (or drawer)", 'convList' in html)
check("Has message list area", 'msgList' in html)
check("Has message input", 'msgInput' in html)
check("Has send button", 'sendBtn' in html)
check("Has loadConversations function", 'loadConversations' in html)
check("Has selectConversation function", 'selectConversation' in html)
check("Has loadMessages function", 'loadMessages' in html)
check("Has sendMessage function", 'sendMessage' in html)
check("Has newConversation function", 'newConversation' in html)
check("Has SSE EventSource usage", 'EventSource' in html)

print("\n=== TC3: Jinja2 template safety ===")
# No Jinja2 escaping issues
check("No Jinja2 escaped quotes in onclick", 'onclick=\\"' not in html)
check("No style backslash bug", "style=\\\\\\'" not in html)
check("No triple escape bug", '\\\\\\\\\\\\\\\"' not in html)
# base.html uses onclick inline (toggleDrawer, closeDrawer) - but those are in base.html
# Check that aichat-specific onclick patterns are clean
assert_newline_escapes = True
for line in html.split('\n'):
    if 'onclick=' in line and ('newConversation' in line or 'sendMessage' in line or 'selectConversation' in line):
        if '\\' in line:
            assert_newline_escapes = False
            break
check("Chat-specific onclick clean (no backslash escaping)", assert_newline_escapes)

print("\n=== TC4: Responsive layout markers ===")
# Check for mobile-responsive CSS
check("Has media query for mobile (<768px)", "@media" in html and "768" in html)
check("Has session sidebar toggle button (mobile)", 'toggle-chat-sidebar' in html or 'toggleChatSidebar' in html or 'toggleSessions' in html)

print("\n=== TC5: API connectivity ===")
# Conversations API
conv_data = curl_json(f"{BASE_URL}/api/conversations")
check("GET /api/conversations returns ok", conv_data and conv_data.get("ok"))
check("GET /api/conversations has data array", isinstance(conv_data.get("data"), list) if conv_data else False)

# Messages API (if conversations exist)
if conv_data and conv_data.get("data"):
    conv_id = conv_data["data"][0]["id"]
    msg_data = curl_json(f"{BASE_URL}/api/conversations/{conv_id}/messages")
    check(f"GET /api/conversations/{conv_id}/messages returns ok",
          msg_data and msg_data.get("ok"))
    if msg_data and msg_data.get("data"):
        m = msg_data["data"][0]
        check("Messages have role field", "role" in m)
        check("Messages have content field", "content" in m)
        check("Messages have created_at field", "created_at" in m)
        check("Messages have id field", "id" in m)

print(f"\n=== {len(FAILURES)} failure(s) ===")
if FAILURES:
    for f in FAILURES:
        print(f"  FAIL: {f}")
    sys.exit(1)
else:
    print("ALL TESTS PASSED")
    sys.exit(0)