新增功能: - explorer.py: AI功能探索器 - 自动发现页面可交互元素 - 元素分类 (navigation/button/link/card/menu) - 危险操作保护 (删除/退出只记录不执行) - DOM快速定位替代AI定位 (速度提升10x) - 站点地图和BUG清单生成 - main.py: 添加 explore() 方法 - generator.py: 添加探索报告生成 (暗色主题+Mermaid站点图) - test_cases.py: 支持 goal/explore/hybrid 三种模式 测试结果: - 成功发现30个可交互元素 - 自动分类: Links(11), Navigation(8), Cards(8), Buttons(2), Menu(1) - 生成完整HTML探索报告
This commit is contained in:
@@ -16,12 +16,39 @@ import time
|
||||
# ============================================================
|
||||
|
||||
TEST_CASES = [
|
||||
# 目标模式: 执行指定目标
|
||||
{
|
||||
"name": "Example.com 链接测试",
|
||||
"name": "登录",
|
||||
"url": "http://47.99.105.253:8084",
|
||||
"mode": "goal", # 目标驱动模式
|
||||
"goal": "填入账号admin 密码password,登录成功",
|
||||
},
|
||||
# 添加更多测试用例...
|
||||
|
||||
# 探索模式: AI 自主发现功能
|
||||
{
|
||||
"name": "功能探索",
|
||||
"url": "http://47.99.105.253:8084",
|
||||
"mode": "explore", # 探索模式
|
||||
"config": {
|
||||
"max_depth": 3,
|
||||
"max_clicks": 30,
|
||||
"dangerous_patterns": ["删除", "移除", "退出", "注销"], # 记录但不执行
|
||||
"require_login": { # 需要先登录
|
||||
"goal": "填入账号admin 密码password,登录成功"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
# 混合模式: 先执行目标,再探索
|
||||
# {
|
||||
# "name": "登录后探索",
|
||||
# "url": "http://47.99.105.253:8084",
|
||||
# "mode": "hybrid",
|
||||
# "steps": [
|
||||
# {"action": "goal", "goal": "登录"},
|
||||
# {"action": "explore", "config": {"max_clicks": 10}}
|
||||
# ]
|
||||
# },
|
||||
]
|
||||
|
||||
|
||||
@@ -34,22 +61,55 @@ def run_single_case(case: Dict[str, Any], model: str = "claude",
|
||||
"""运行单个测试用例(独立浏览器实例)"""
|
||||
name = case.get("name", "Unknown")
|
||||
url = case["url"]
|
||||
goal = case["goal"]
|
||||
mode = case.get("mode", "goal")
|
||||
|
||||
result = {
|
||||
"name": name,
|
||||
"url": url,
|
||||
"goal": goal,
|
||||
"mode": mode,
|
||||
"status": "failed",
|
||||
}
|
||||
|
||||
try:
|
||||
with WebTester(model=model, headless=headless) as tester:
|
||||
tester.goto(url)
|
||||
test_result = tester.test(goal)
|
||||
result["status"] = "passed"
|
||||
result["steps"] = test_result["steps"]
|
||||
result["report"] = test_result["report"]
|
||||
|
||||
if mode == "goal":
|
||||
# 目标模式
|
||||
goal = case.get("goal", "")
|
||||
test_result = tester.test(goal)
|
||||
result["status"] = "passed"
|
||||
result["steps"] = test_result["steps"]
|
||||
result["report"] = test_result["report"]
|
||||
|
||||
elif mode == "explore":
|
||||
# 探索模式
|
||||
config = case.get("config", {})
|
||||
|
||||
# 如果需要先登录
|
||||
require_login = config.pop("require_login", None)
|
||||
if require_login:
|
||||
login_goal = require_login.get("goal", "")
|
||||
if login_goal:
|
||||
tester.test(login_goal)
|
||||
tester.browser.wait(1000)
|
||||
|
||||
# 执行探索
|
||||
explore_result = tester.explore(config)
|
||||
result["status"] = "passed"
|
||||
result["elements"] = len(explore_result.get("discovered_elements", []))
|
||||
result["bugs"] = len(explore_result.get("bug_list", []))
|
||||
result["report"] = explore_result.get("report", "")
|
||||
|
||||
elif mode == "hybrid":
|
||||
# 混合模式
|
||||
for step in case.get("steps", []):
|
||||
if step.get("action") == "goal":
|
||||
tester.test(step["goal"])
|
||||
elif step.get("action") == "explore":
|
||||
tester.explore(step.get("config", {}))
|
||||
result["status"] = "passed"
|
||||
|
||||
except Exception as e:
|
||||
result["error"] = str(e)
|
||||
|
||||
@@ -64,37 +124,71 @@ def run_tests(model: str = "claude", headless: bool = False):
|
||||
for i, case in enumerate(TEST_CASES, 1):
|
||||
name = case.get("name", f"Test {i}")
|
||||
url = case["url"]
|
||||
goal = case["goal"]
|
||||
mode = case.get("mode", "goal")
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"🧪 [{i}/{len(TEST_CASES)}] {name}")
|
||||
print(f" URL: {url}")
|
||||
print(f" Goal: {goal}")
|
||||
print(f" Mode: {mode}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
try:
|
||||
tester.goto(url)
|
||||
result = tester.test(goal)
|
||||
|
||||
# 检查所有步骤是否成功
|
||||
all_passed = all(r.get("success", False) for r in result.get("results", []))
|
||||
failed_count = sum(1 for r in result.get("results", []) if not r.get("success", False))
|
||||
|
||||
if all_passed:
|
||||
print(f"✅ 完成: {result['steps']} 步骤")
|
||||
status = "passed"
|
||||
else:
|
||||
print(f"⚠️ 部分失败: {failed_count}/{result['steps']} 步骤失败")
|
||||
status = "failed"
|
||||
|
||||
print(f"📄 报告: {result['report']}")
|
||||
|
||||
results.append({
|
||||
"name": name,
|
||||
"status": status,
|
||||
"steps": result["steps"],
|
||||
"report": result["report"],
|
||||
})
|
||||
if mode == "goal":
|
||||
goal = case.get("goal", "")
|
||||
result = tester.test(goal)
|
||||
|
||||
# 检查所有步骤是否成功
|
||||
all_passed = all(r.get("success", False) for r in result.get("results", []))
|
||||
failed_count = sum(1 for r in result.get("results", []) if not r.get("success", False))
|
||||
|
||||
if all_passed:
|
||||
print(f"✅ 完成: {result['steps']} 步骤")
|
||||
status = "passed"
|
||||
else:
|
||||
print(f"⚠️ 部分失败: {failed_count}/{result['steps']} 步骤失败")
|
||||
status = "failed"
|
||||
|
||||
print(f"📄 报告: {result['report']}")
|
||||
|
||||
results.append({
|
||||
"name": name,
|
||||
"status": status,
|
||||
"steps": result["steps"],
|
||||
"report": result["report"],
|
||||
})
|
||||
|
||||
elif mode == "explore":
|
||||
config = case.get("config", {}).copy()
|
||||
|
||||
# 如果需要先登录
|
||||
require_login = config.pop("require_login", None)
|
||||
if require_login:
|
||||
login_goal = require_login.get("goal", "")
|
||||
if login_goal:
|
||||
print(f" 🔐 执行登录...")
|
||||
tester.test(login_goal)
|
||||
tester.browser.wait(1000)
|
||||
|
||||
# 执行探索
|
||||
print(f" 🔍 开始功能探索...")
|
||||
result = tester.explore(config)
|
||||
|
||||
elements = len(result.get("discovered_elements", []))
|
||||
bugs = len(result.get("bug_list", []))
|
||||
|
||||
print(f"✅ 探索完成: 发现 {elements} 个元素, {bugs} 个问题")
|
||||
print(f"📄 报告: {result.get('report', '')}")
|
||||
|
||||
results.append({
|
||||
"name": name,
|
||||
"status": "passed",
|
||||
"elements": elements,
|
||||
"bugs": bugs,
|
||||
"report": result.get("report", ""),
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 失败: {e}")
|
||||
results.append({
|
||||
|
||||
Reference in New Issue
Block a user