143 lines
5.7 KiB
Python
143 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Playwright test to debug modal click functionality on live LLM Monitor instance
|
|
"""
|
|
import asyncio
|
|
import json
|
|
from playwright.async_api import async_playwright
|
|
|
|
TARGET_URL = "http://192.168.254.12:8000"
|
|
|
|
async def test_modal_click():
|
|
async with async_playwright() as p:
|
|
browser = await p.chromium.launch(headless=True)
|
|
context = await browser.new_context()
|
|
page = await context.new_page()
|
|
|
|
# Enable console logging
|
|
page.on("console", lambda msg: print(f"[CONSOLE] {msg.type}: {msg.text}"))
|
|
page.on("pageerror", lambda exc: print(f"[PAGE ERROR] {exc}"))
|
|
|
|
print(f"\n🔍 [TEST] Navigating to {TARGET_URL}...")
|
|
await page.goto(TARGET_URL, wait_until="networkidle")
|
|
|
|
# Wait for models to load
|
|
print("[WAIT] Waiting for models container to populate...")
|
|
try:
|
|
await page.wait_for_selector("[data-model-key]", timeout=5000)
|
|
print("✅ [SUCCESS] Models loaded - found cards with data-model-key attribute")
|
|
except Exception as e:
|
|
print(f"❌ [FAILURE] No models found: {e}")
|
|
print("\n📋 Page content:")
|
|
content = await page.content()
|
|
print(content[:2000])
|
|
await browser.close()
|
|
return
|
|
|
|
# Get all model cards
|
|
cards = await page.query_selector_all("[data-model-key]")
|
|
print(f"\n📊 Found {len(cards)} model cards")
|
|
|
|
if len(cards) == 0:
|
|
print("❌ No cards found after waiting")
|
|
await browser.close()
|
|
return
|
|
|
|
# Get first card info
|
|
first_card = cards[0]
|
|
model_key = await first_card.get_attribute("data-model-key")
|
|
print(f"\n🎯 [TARGET] First card data-model-key: {model_key}")
|
|
|
|
# Get modal state BEFORE click
|
|
modal_before = await page.query_selector("#model-details-modal")
|
|
if modal_before:
|
|
hidden_class_before = await modal_before.get_attribute("class")
|
|
is_hidden_before = "hidden" in (hidden_class_before or "")
|
|
print(f"\n📍 [BEFORE CLICK] Modal HTML class: '{hidden_class_before}'")
|
|
print(f" Is hidden: {is_hidden_before}")
|
|
else:
|
|
print("\n❌ [ERROR] Modal element #model-details-modal not found!")
|
|
|
|
# Check if app is initialized
|
|
app_exists = await page.evaluate("typeof window.llmMonitorApp !== 'undefined'")
|
|
print(f" App initialized: {app_exists}")
|
|
|
|
# Check localStorage
|
|
localStorage = await page.evaluate("JSON.stringify(localStorage)")
|
|
print(f" localStorage keys: {list(json.loads(localStorage).keys())}")
|
|
|
|
# PERFORM CLICK
|
|
print(f"\n🖱️ [CLICK] Clicking on model card...")
|
|
await first_card.click()
|
|
|
|
# Brief pause for DOM update
|
|
await page.wait_for_timeout(500)
|
|
|
|
# Get modal state AFTER click
|
|
modal_after = await page.query_selector("#model-details-modal")
|
|
if modal_after:
|
|
hidden_class_after = await modal_after.get_attribute("class")
|
|
is_hidden_after = "hidden" in (hidden_class_after or "")
|
|
print(f"\n📍 [AFTER CLICK] Modal HTML class: '{hidden_class_after}'")
|
|
print(f" Is hidden: {is_hidden_after}")
|
|
else:
|
|
print("\n❌ [ERROR] Modal element #model-details-modal not found after click!")
|
|
|
|
# Evaluate if modal is visible
|
|
is_visible = await page.evaluate("""
|
|
() => {
|
|
const modal = document.getElementById('model-details-modal');
|
|
if (!modal) return 'NOT_FOUND';
|
|
const isHidden = modal.classList.contains('hidden');
|
|
const computedStyle = window.getComputedStyle(modal);
|
|
return {
|
|
hasHiddenClass: isHidden,
|
|
display: computedStyle.display,
|
|
visibility: computedStyle.visibility,
|
|
opacity: computedStyle.opacity,
|
|
zIndex: computedStyle.zIndex
|
|
};
|
|
}
|
|
""")
|
|
print(f"\n💻 [DOM CHECK] Modal computed state:\n {json.dumps(is_visible, indent=3)}")
|
|
|
|
# Check for event listeners
|
|
listeners = await page.evaluate("""
|
|
() => {
|
|
const card = document.querySelector('[data-model-key]');
|
|
if (!card) return 'NO_CARDS';
|
|
|
|
// Try to trigger click manually via app if it exists
|
|
if (window.llmMonitorApp && typeof window.llmMonitorApp.showModelDetails === 'function') {
|
|
return 'APP_METHOD_EXISTS';
|
|
}
|
|
return 'NO_APP_METHOD';
|
|
}
|
|
""")
|
|
print(f"\n🔗 [LISTENERS] Event handler state: {listeners}")
|
|
|
|
# Take screenshot
|
|
screenshot_path = "/tmp/modal-test-after-click.png"
|
|
await page.screenshot(path=screenshot_path)
|
|
print(f"\n📸 Screenshot saved: {screenshot_path}")
|
|
|
|
# Final verdict
|
|
print("\n" + "="*70)
|
|
if hidden_class_before is not None and hidden_class_after is not None:
|
|
if is_hidden_before and not is_hidden_after:
|
|
print("✅ [PASS] Modal successfully opened on click!")
|
|
elif is_hidden_before and is_hidden_after:
|
|
print("❌ [FAIL] Modal still has 'hidden' class after click")
|
|
print(" → Event handler may not be attached")
|
|
print(" → app.js may not be loaded/initialized")
|
|
else:
|
|
print("⚠️ [UNCLEAR] Unexpected modal state")
|
|
else:
|
|
print("❌ [ERROR] Could not compare modal states")
|
|
print("="*70)
|
|
|
|
await browser.close()
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(test_modal_click())
|