Files
NextEdu/scripts/test-failing-modules.py
SpecialX 9783be58c0 feat(scripts): add diagnostic, seed, and test scripts
- Add add-ai-provider-visibility and add-missing-columns migration scripts

- Add clear-error-book, seed-error-book, diagnose-error-book scripts

- Add diagnose-tables and create-missing-tables scripts

- Add test-failing-modules and test-teacher-pages test scripts
2026-06-24 12:01:54 +08:00

152 lines
6.0 KiB
Python

"""
检查学情诊断、错题分析、公告、消息 4 个模块的实际渲染状态
截取截图 + 捕获控制台错误 + 检查页面内容
"""
import os
import time
from playwright.sync_api import sync_playwright
BASE_URL = "http://localhost:3000"
TEACHER_EMAIL = "t_chinese_1@xiaoxue.edu.cn"
TEACHER_PASSWORD = "123456"
SCREENSHOT_DIR = os.path.join(os.path.dirname(__file__), "..", "bugs", "screenshots")
os.makedirs(SCREENSHOT_DIR, exist_ok=True)
ROUTES = [
("/teacher/diagnostic", "学情诊断"),
("/teacher/error-book", "错题分析"),
("/announcements", "公告"),
("/messages", "消息"),
]
def main():
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(viewport={"width": 1440, "height": 900})
page = context.new_page()
# Login
print(">>> 登录...")
page.goto(f"{BASE_URL}/login", wait_until="domcontentloaded")
page.wait_for_timeout(3000)
page.locator('input[name="email"]').fill(TEACHER_EMAIL)
page.locator('input[type="password"]').first.fill(TEACHER_PASSWORD)
page.evaluate("""() => {
const form = document.querySelector('form');
if (form) {
const event = new Event('submit', { cancelable: true, bubbles: true });
form.dispatchEvent(event);
}
}""")
page.wait_for_timeout(5000)
print(f"登录后 URL: {page.url}")
for route, name in ROUTES:
print(f"\n{'='*60}")
print(f">>> 测试: {name} ({route})")
print(f"{'='*60}")
console_errors = []
console_warnings = []
def on_console(msg):
if msg.type == "error":
console_errors.append(msg.text)
elif msg.type == "warning":
console_warnings.append(msg.text)
def on_page_error(err):
console_errors.append(f"PageError: {str(err)[:300]}")
page.on("console", on_console)
page.on("pageerror", on_page_error)
try:
response = page.goto(f"{BASE_URL}{route}", timeout=30000, wait_until="domcontentloaded")
page.wait_for_timeout(3000) # Wait for client-side JS to execute
print(f" HTTP Status: {response.status if response else 'N/A'}")
print(f" Final URL: {page.url}")
# Check for error indicators on page
body_text = page.locator("body").text_content() or ""
print(f" Body text length: {len(body_text)}")
# Check for common error patterns
error_elements = page.locator('[role="alert"], .text-destructive, .text-red-500, .text-red-600')
error_count = error_elements.count()
if error_count > 0:
print(f" Error elements on page: {error_count}")
for i in range(min(error_count, 5)):
try:
text = error_elements.nth(i).text_content()
if text and text.strip():
print(f" [{i}] {text.strip()[:200]}")
except:
pass
# Check for loading spinners still visible
spinners = page.locator('.animate-spin, [role="status"], .loading')
spinner_count = spinners.count()
if spinner_count > 0:
print(f" Loading spinners still visible: {spinner_count}")
# Check for empty state
if "暂无" in body_text or "No data" in body_text or "没有" in body_text:
# Find the context
for keyword in ["暂无", "No data", "没有"]:
idx = body_text.find(keyword)
if idx >= 0:
context_text = body_text[max(0, idx-30):idx+80]
print(f" Empty state: ...{context_text}...")
break
# Check for "加载失败" or "error" text
for keyword in ["加载失败", "load", "error", "错误", "失败", "Error"]:
if keyword.lower() in body_text.lower():
idx = body_text.lower().find(keyword.lower())
context_text = body_text[max(0, idx-30):idx+100]
print(f" Found '{keyword}': ...{context_text}...")
break
# Take screenshot
screenshot_name = route.replace("/", "_").strip("_") + ".png"
screenshot_path = os.path.join(SCREENSHOT_DIR, screenshot_name)
page.screenshot(path=screenshot_path, full_page=True)
print(f" Screenshot: {screenshot_path}")
# Print console errors
if console_errors:
print(f"\n Console Errors ({len(console_errors)}):")
for err in console_errors[:10]:
print(f" - {err[:300]}")
else:
print(f" Console Errors: 0")
if console_warnings:
print(f"\n Console Warnings ({len(console_warnings)}):")
for w in console_warnings[:5]:
print(f" - {w[:200]}")
except Exception as e:
print(f" EXCEPTION: {type(e).__name__}: {str(e)[:300]}")
screenshot_name = route.replace("/", "_").strip("_") + "_error.png"
screenshot_path = os.path.join(SCREENSHOT_DIR, screenshot_name)
try:
page.screenshot(path=screenshot_path, full_page=True)
except:
pass
finally:
page.remove_listener("console", on_console)
page.remove_listener("pageerror", on_page_error)
browser.close()
print(f"\n{'='*60}")
print("测试完成!")
print(f"{'='*60}")
if __name__ == "__main__":
main()