Add modal click Playwright test utilities

This commit is contained in:
Luca Sacchi Ricciardi
2026-04-25 14:22:36 +02:00
parent eea6e2a80e
commit 32302e2b06
3 changed files with 297 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
{
"name": "llm-monitor-test",
"version": "1.0.0",
"type": "commonjs",
"dependencies": {
"playwright": "^1.59.1"
}
}
+147
View File
@@ -0,0 +1,147 @@
// test_modal_click.js
// Playwright test to debug modal click functionality
const { chromium } = require('playwright');
const TARGET_URL = 'http://192.168.254.12:8000';
(async () => {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();
// Log console messages
page.on('console', msg => console.log(`[CONSOLE] ${msg.type()}: ${msg.text()}`));
page.on('pageerror', exc => console.log(`[PAGE ERROR] ${exc.message}`));
try {
console.log(`\n🔍 [TEST] Navigating to ${TARGET_URL}...`);
await page.goto(TARGET_URL, { waitUntil: 'networkidle' });
console.log('✅ Page loaded');
console.log('[WAIT] Waiting for models container to populate...');
try {
await page.waitForSelector('[data-model-key]', { timeout: 5000 });
console.log('✅ [SUCCESS] Models loaded - found cards with data-model-key attribute');
} catch (e) {
console.log(`❌ [FAILURE] No models found: ${e.message}`);
const content = await page.content();
console.log('\n📋 Page content (first 1000 chars):');
console.log(content.substring(0, 1000));
await browser.close();
return;
}
// Get all model cards
const cards = await page.locator('[data-model-key]').all();
console.log(`\n📊 Found ${cards.length} model cards`);
if (cards.length === 0) {
console.log('❌ No cards found');
await browser.close();
return;
}
// Get first card info
const firstCard = cards[0];
const modelKey = await firstCard.getAttribute('data-model-key');
console.log(`\n🎯 [TARGET] First card data-model-key: ${modelKey}`);
// Get modal state BEFORE click
const modalElement = page.locator('#model-details-modal');
const modalExists = await modalElement.count() > 0;
if (!modalExists) {
console.log('\n❌ [ERROR] Modal element #model-details-modal not found!');
await browser.close();
return;
}
const classAttributeBefore = await modalElement.getAttribute('class');
const isHiddenBefore = classAttributeBefore && classAttributeBefore.includes('hidden');
console.log(`\n📍 [BEFORE CLICK] Modal HTML class: '${classAttributeBefore}'`);
console.log(` Is hidden: ${isHiddenBefore}`);
// Check if app is initialized
const appExists = await page.evaluate(() => typeof window.llmMonitorApp !== 'undefined');
console.log(` App initialized: ${appExists}`);
// Check localStorage
const localStorage = await page.evaluate(() => JSON.stringify(Object.fromEntries(Object.entries(window.localStorage))));
const lsData = JSON.parse(localStorage);
console.log(` localStorage keys: ${Object.keys(lsData).join(', ')}`);
// PERFORM CLICK
console.log(`\n🖱️ [CLICK] Clicking on model card...`);
await firstCard.click();
// Brief pause for DOM update
await page.waitForTimeout(500);
// Get modal state AFTER click
const classAttributeAfter = await modalElement.getAttribute('class');
const isHiddenAfter = classAttributeAfter && classAttributeAfter.includes('hidden');
console.log(`\n📍 [AFTER CLICK] Modal HTML class: '${classAttributeAfter}'`);
console.log(` Is hidden: ${isHiddenAfter}`);
// Evaluate if modal is visible
const modalState = 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
};
});
console.log(`\n💻 [DOM CHECK] Modal computed state:`);
console.log(JSON.stringify(modalState, null, 3));
// Check for event listeners and app methods
const appState = await page.evaluate(() => {
const card = document.querySelector('[data-model-key]');
if (!card) return 'NO_CARDS';
if (window.llmMonitorApp && typeof window.llmMonitorApp.showModelDetails === 'function') {
return 'APP_METHOD_EXISTS';
}
return 'NO_APP_METHOD';
});
console.log(`\n🔗 [LISTENERS] Event handler state: ${appState}`);
// Take screenshot
const screenshotPath = '/tmp/modal-test-after-click.png';
await page.screenshot({ path: screenshotPath });
console.log(`\n📸 Screenshot saved: ${screenshotPath}`);
// Final verdict
console.log('\n' + '='.repeat(70));
if (isHiddenBefore !== undefined && isHiddenAfter !== undefined) {
if (isHiddenBefore && !isHiddenAfter) {
console.log('✅ [PASS] Modal successfully opened on click!');
} else if (isHiddenBefore && isHiddenAfter) {
console.log('❌ [FAIL] Modal still has "hidden" class after click');
console.log(' → Event handler may not be attached');
console.log(' → app.js may not be loaded/initialized');
} else {
console.log('⚠️ [UNCLEAR] Unexpected modal state');
}
} else {
console.log('❌ [ERROR] Could not compare modal states');
}
console.log('='.repeat(70));
} catch (error) {
console.error('Test error:', error.message);
} finally {
await browser.close();
}
})();
+142
View File
@@ -0,0 +1,142 @@
#!/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())