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
This commit is contained in:
151
scripts/test-failing-modules.py
Normal file
151
scripts/test-failing-modules.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
检查学情诊断、错题分析、公告、消息 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()
|
||||
Reference in New Issue
Block a user