本地 RAG 知识库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

259 lines
9.3 KiB

# -*- coding: utf-8 -*-
"""
Automated test script for Vue 3 frontend app
"""
from playwright.sync_api import sync_playwright
import json
import sys
import io
# Force UTF-8 output
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
CONSOLE_ERRORS = []
RESOURCE_ERRORS = []
def run():
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(
viewport={'width': 1440, 'height': 900},
ignore_https_errors=True,
)
page = context.new_page()
# Collect errors
page.on('console', lambda msg: CONSOLE_ERRORS.append({
'type': msg.type,
'text': msg.text,
}) if msg.type in ('error', 'warning') else None)
page.on('pageerror', lambda err: CONSOLE_ERRORS.append({
'type': 'pageerror',
'text': str(err),
}))
page.on('requestfailed', lambda req: RESOURCE_ERRORS.append({
'url': req.url,
'failure': req.failure,
}) if req.failure else None)
# ========== 1. Home page (login) ==========
print('\n' + '='*60)
print('TEST 1: Home page (login)')
print('='*60)
page.goto('http://localhost:5173', wait_until='networkidle', timeout=30000)
page.wait_for_timeout(2000)
page.screenshot(path='/tmp/test_01_login.png', full_page=True)
title = page.title()
print(f'Page title: {title}')
body_text = page.inner_text('body')
print(f'Body preview: {body_text[:500]}')
# Check login form elements
inputs = page.locator('input').all()
print(f'Input count: {len(inputs)}')
for i, inp in enumerate(inputs):
placeholder = inp.get_attribute('placeholder') or ''
input_type = inp.get_attribute('type') or 'text'
print(f' Input {i}: type={input_type}, placeholder="{placeholder}"')
buttons = page.locator('button').all()
print(f'Button count: {len(buttons)}')
for i, btn in enumerate(buttons):
text = btn.inner_text()
print(f' Button {i}: text="{text}"')
# Try login
try:
username_input = page.locator('input[type="text"]').first
password_input = page.locator('input[type="password"]').first
username_input.fill('admin')
password_input.fill('admin123')
except Exception as e:
print(f'Fill form error: {e}')
# Click any button containing login text
login_clicked = False
for btn in page.locator('button').all():
text = btn.inner_text().strip()
if '' in text or 'login' in text.lower():
btn.click()
login_clicked = True
print(f'Clicked login button: "{text}"')
break
if not login_clicked:
# Try pressing Enter instead
page.keyboard.press('Enter')
print('Pressed Enter to submit form')
page.wait_for_timeout(3000)
page.screenshot(path='/tmp/test_02_after_login.png', full_page=True)
current_url = page.evaluate('window.location.href')
print(f'Current URL: {current_url}')
current_hash = page.evaluate('window.location.hash')
print(f'Current hash: {current_hash}')
# Check if we're still on login page or navigated away
body_text = page.inner_text('body')
print(f'After login body preview: {body_text[:500]}')
# ========== 2. Navigate all pages ==========
routes = [
'/chat',
'/knowledge/stats',
'/knowledge/document',
'/knowledge/category',
'/knowledge/search',
'/knowledge/faq',
'/conversation',
'/dashboard',
'/settings/role',
'/settings/model-config',
'/settings/sensitive',
'/settings/audit-log',
'/settings/user',
'/settings/api-key',
'/settings/webhook',
'/settings/mcp-server',
'/settings/pipeline-flow',
'/settings/system-config',
]
for route in routes:
print(f'\n--- Navigate: {route} ---')
page.evaluate(f'window.location.hash = "{route}"')
page.wait_for_timeout(2000)
safe_name = route.replace('/', '_')
page.screenshot(path=f'/tmp/route_{safe_name}.png', full_page=True)
# Count interactive elements on page
btns = page.locator('button').count()
inputs = page.locator('input:not([type="hidden"])').count()
selects = page.locator('.t-select, .t-select__wrap').count()
tables = page.locator('.t-table, table').count()
print(f' Buttons:{btns} Inputs:{inputs} Selects:{selects} Tables:{tables}')
# ========== 3. Deep check: Model Config page ==========
print('\n' + '='*60)
print('TEST 3: Model Config deep check')
print('='*60)
page.evaluate('window.location.hash = "/settings/model-config"')
page.wait_for_timeout(3000)
page.screenshot(path='/tmp/deep_modelconfig.png', full_page=True)
# Find and click any "add" button
for btn in page.locator('button').all():
try:
text = btn.inner_text().strip()
except:
continue
if '' in text or '' in text or 'add' in text.lower():
try:
btn.click()
page.wait_for_timeout(1000)
page.screenshot(path='/tmp/modelconfig_dialog.png', full_page=True)
print(f'Opened dialog via button: "{text}"')
# Check selects in dialog
dialog_selects = page.locator('.t-dialog .t-select, .t-dialog .t-select__wrap').all()
print(f' Dialog select count: {len(dialog_selects)}')
for ds in dialog_selects[:5]:
try:
ds.click()
page.wait_for_timeout(500)
opts = page.locator('.t-select-option, .t-popup__content .t-select-option').all()
print(f' Options visible: {len(opts)}')
page.keyboard.press('Escape')
page.wait_for_timeout(300)
except Exception as e:
print(f' Select click error: {e}')
page.keyboard.press('Escape')
page.wait_for_timeout(500)
except Exception as e:
print(f' Button click error: {e}')
break
# ========== 4. Deep check: Doc List page ==========
print('\n' + '='*60)
print('TEST 4: Doc List page')
print('='*60)
page.evaluate('window.location.hash = "/knowledge/document"')
page.wait_for_timeout(3000)
page.screenshot(path='/tmp/deep_doclist.png', full_page=True)
# ========== 5. Deep check: FAQ page ==========
print('\n' + '='*60)
print('TEST 5: FAQ Management page')
print('='*60)
page.evaluate('window.location.hash = "/knowledge/faq"')
page.wait_for_timeout(3000)
page.screenshot(path='/tmp/deep_faq.png', full_page=True)
# ========== 6. Deep check: User Manager page ==========
print('\n' + '='*60)
print('TEST 6: User Manager page')
print('='*60)
page.evaluate('window.location.hash = "/settings/user"')
page.wait_for_timeout(3000)
page.screenshot(path='/tmp/deep_user.png', full_page=True)
# ========== 7. Final Report ==========
print('\n' + '='*60)
print('FINAL REPORT')
print('='*60)
# Categorize errors
real_errors = [e for e in CONSOLE_ERRORS if e['type'] in ('error', 'pageerror')]
warnings = [e for e in CONSOLE_ERRORS if e['type'] == 'warning']
# Deduplicate
seen = set()
unique_errors = []
for e in real_errors:
key = e['text'][:100]
if key not in seen:
seen.add(key)
unique_errors.append(e)
print(f'\nConsole Errors (unique): {len(unique_errors)}')
for e in unique_errors:
print(f' [{e["type"]}] {e["text"][:300]}')
print(f'\nConsole Warnings (total): {len(warnings)}')
seen_w = set()
for w in warnings:
key = w['text'][:100]
if key not in seen_w:
seen_w.add(key)
print(f' {w["text"][:200]}')
failed = [r for r in RESOURCE_ERRORS if r.get('failure')]
print(f'\nFailed Resources: {len(failed)}')
for r in failed[:20]:
print(f' {r["url"]}')
browser.close()
# Save report
report = {
'errors': unique_errors,
'warnings': [w['text'] for w in warnings],
'failed_resources': [r['url'] for r in failed],
}
with open('/tmp/test_report.json', 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print('\nReport saved to /tmp/test_report.json')
if __name__ == '__main__':
run()