feat: 添加AI主动探索测试模式
Some checks failed
AI Web Tester CI / test (push) Has been cancelled

新增功能:
- 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:
empty
2025-12-28 20:39:15 +08:00
parent 2a22708ab5
commit c6def51435
4 changed files with 748 additions and 30 deletions

View File

@@ -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({